Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6af6dfb6 authored by Joe Bolinger's avatar Joe Bolinger
Browse files

Propogate face unlock always require confirmation setting to biometric prompt.

This signal is also piped through via the framework, but it was unused/broken. Adds a repo component to create a single source of truth for the setting and elimiate redunant caches of this value (to cleanup and remove in qpr).

Fix: 286265826
Test: manual (use BP test app and toggle require confirmation button + "always require confirmation" in Settings -> Face Unlock with coex and face only)
Test: atest com.android.systemui.biometrics.data.repository  com.android.systemui.biometrics.ui.viewmodel
Test: atest AuthControllerTest
Change-Id: Iba49d9dbb5411bc65c52297d82c1e6d5ee020e46
parent a944e547
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -374,7 +374,6 @@ public class AuthContainerView extends LinearLayout
        if (Utils.isBiometricAllowed(config.mPromptInfo)) {
            mPromptSelectorInteractorProvider.get().useBiometricsForAuthentication(
                    config.mPromptInfo,
                    config.mRequireConfirmation,
                    config.mUserId,
                    config.mOperationId,
                    new BiometricModalities(fpProps, faceProps));
+3 −0
Original line number Diff line number Diff line
@@ -1208,8 +1208,11 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,

        final PromptInfo promptInfo = (PromptInfo) args.arg1;
        final int[] sensorIds = (int[]) args.arg3;

        // TODO(b/251476085): remove these unused parameters (replaced with SSOT elsewhere)
        final boolean credentialAllowed = (boolean) args.arg4;
        final boolean requireConfirmation = (boolean) args.arg5;

        final int userId = args.argi1;
        final String opPackageName = (String) args.arg6;
        final long operationId = args.argl1;
+6 −0
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.systemui.biometrics.dagger

import com.android.settingslib.udfps.UdfpsUtils
import com.android.systemui.biometrics.data.repository.FaceSettingsRepository
import com.android.systemui.biometrics.data.repository.FaceSettingsRepositoryImpl
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepositoryImpl
import com.android.systemui.biometrics.data.repository.PromptRepository
@@ -45,6 +47,10 @@ import javax.inject.Qualifier
@Module
interface BiometricsModule {

    @Binds
    @SysUISingleton
    fun faceSettings(impl: FaceSettingsRepositoryImpl): FaceSettingsRepository

    @Binds
    @SysUISingleton
    fun biometricPromptRepository(impl: PromptRepositoryImpl): PromptRepository
+57 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.biometrics.data.repository

import android.os.Handler
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.util.settings.SecureSettings
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject

/**
 * Repository for the global state of users Face Unlock preferences.
 *
 * Largely a wrapper around [SecureSettings]'s proxy to Settings.Secure.
 */
interface FaceSettingsRepository {

    /** Get Settings for the given user [id]. */
    fun forUser(id: Int?): FaceUserSettingsRepository
}

@SysUISingleton
class FaceSettingsRepositoryImpl
@Inject
constructor(
    @Main private val mainHandler: Handler,
    private val secureSettings: SecureSettings,
) : FaceSettingsRepository {

    private val userSettings = ConcurrentHashMap<Int, FaceUserSettingsRepository>()

    override fun forUser(id: Int?): FaceUserSettingsRepository =
        if (id != null) {
            userSettings.computeIfAbsent(id) { _ ->
                FaceUserSettingsRepositoryImpl(id, mainHandler, secureSettings).also { repo ->
                    repo.start()
                }
            }
        } else {
            FaceUserSettingsRepositoryImpl.Empty
        }
}
+88 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.biometrics.data.repository

import android.database.ContentObserver
import android.os.Handler
import android.provider.Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.util.settings.SecureSettings
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flowOf

/** Settings for a user. */
interface FaceUserSettingsRepository {
    /** The user's id. */
    val userId: Int

    /** If BiometricPrompt should always require confirmation (overrides app's preference). */
    val alwaysRequireConfirmationInApps: Flow<Boolean>
}

class FaceUserSettingsRepositoryImpl(
    override val userId: Int,
    @Main private val mainHandler: Handler,
    private val secureSettings: SecureSettings,
) : FaceUserSettingsRepository {

    /** Indefinitely subscribe to user preference changes. */
    fun start() {
        watch(
            FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
            _alwaysRequireConfirmationInApps,
        )
    }

    private var _alwaysRequireConfirmationInApps = MutableStateFlow(false)
    override val alwaysRequireConfirmationInApps: Flow<Boolean> =
        _alwaysRequireConfirmationInApps.asStateFlow()

    /** Defaults to use when no user is specified. */
    object Empty : FaceUserSettingsRepository {
        override val userId = -1
        override val alwaysRequireConfirmationInApps = flowOf(false)
    }

    private fun watch(
        key: String,
        toUpdate: MutableStateFlow<Boolean>,
        defaultValue: Boolean = false,
    ) = secureSettings.watch(userId, mainHandler, key, defaultValue) { v -> toUpdate.value = v }
}

private fun SecureSettings.watch(
    userId: Int,
    handler: Handler,
    key: String,
    defaultValue: Boolean = false,
    onChange: (Boolean) -> Unit,
) {
    fun fetch(): Boolean = getIntForUser(key, if (defaultValue) 1 else 0, userId) > 0

    registerContentObserverForUser(
        key,
        false /* notifyForDescendants */,
        object : ContentObserver(handler) {
            override fun onChange(selfChange: Boolean) = onChange(fetch())
        },
        userId
    )

    onChange(fetch())
}
Loading