diff options
12 files changed, 430 insertions, 287 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt index 1817ea9024fc..64bf688b3c5d 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt @@ -36,7 +36,6 @@ import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINL import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST -import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.R.string.bouncer_face_not_recognized import com.android.systemui.R.string.keyguard_enter_password import com.android.systemui.R.string.keyguard_enter_pattern @@ -80,13 +79,14 @@ import com.android.systemui.R.string.kg_wrong_pin_try_again import com.android.systemui.bouncer.shared.model.BouncerMessageModel import com.android.systemui.bouncer.shared.model.Message import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import javax.inject.Inject @SysUISingleton class BouncerMessageFactory @Inject constructor( - private val updateMonitor: KeyguardUpdateMonitor, + private val biometricSettingsRepository: BiometricSettingsRepository, private val securityModel: KeyguardSecurityModel, ) { @@ -99,7 +99,7 @@ constructor( getBouncerMessage( reason, securityModel.getSecurityMode(userId), - updateMonitor.isUnlockingWithFingerprintAllowed + biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value ) return pair?.let { BouncerMessageModel( diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt index 6fb0d4cc5e13..97c1bdb180a1 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt @@ -123,20 +123,11 @@ constructor( fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository, ) : BouncerMessageRepository { - private val isFaceEnrolledAndEnabled = - and( - biometricSettingsRepository.isFaceAuthenticationEnabled, - biometricSettingsRepository.isFaceEnrolled - ) - - private val isFingerprintEnrolledAndEnabled = - and( - biometricSettingsRepository.isFingerprintEnabledByDevicePolicy, - biometricSettingsRepository.isFingerprintEnrolled - ) - private val isAnyBiometricsEnabledAndEnrolled = - or(isFaceEnrolledAndEnabled, isFingerprintEnrolledAndEnabled) + or( + biometricSettingsRepository.isFaceAuthEnrolledAndEnabled, + biometricSettingsRepository.isFingerprintEnrolledAndEnabled, + ) private val wasRebootedForMainlineUpdate get() = systemPropertiesHelper.get(SYS_BOOT_REASON_PROP) == REBOOT_MAINLINE_UPDATE @@ -335,8 +326,5 @@ constructor( } } -private fun and(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) = - flow.combine(anotherFlow) { a, b -> a && b } - private fun or(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) = flow.combine(anotherFlow) { a, b -> a || b } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt index 98ae54b1340e..9a7fec1daae0 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt @@ -83,9 +83,7 @@ constructor( fun canShowAlternateBouncerForFingerprint(): Boolean { return bouncerRepository.alternateBouncerUIAvailable.value && - biometricSettingsRepository.isFingerprintEnrolled.value && - biometricSettingsRepository.isStrongBiometricAllowed.value && - biometricSettingsRepository.isFingerprintEnabledByDevicePolicy.value && + biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value && !keyguardUpdateMonitor.isFingerprintLockedOut && !keyguardStateController.isUnlocked && !statusBarStateController.isDozing diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt index ff3e77c46f1c..682e841c3521 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt @@ -28,6 +28,9 @@ import com.android.internal.widget.LockPatternUtils import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.biometrics.AuthController +import com.android.systemui.biometrics.data.repository.FacePropertyRepository +import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository +import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow @@ -68,34 +71,32 @@ import kotlinx.coroutines.flow.transformLatest * upstream changes. */ interface BiometricSettingsRepository { - /** Whether any fingerprints are enrolled for the current user. */ - val isFingerprintEnrolled: StateFlow<Boolean> - - /** Whether face authentication is enrolled for the current user. */ - val isFaceEnrolled: Flow<Boolean> - /** - * Whether face authentication is enabled/disabled based on system settings like device policy, - * biometrics setting. + * If the current user can enter the device using fingerprint. This is true if user has enrolled + * fingerprints and fingerprint auth is not disabled through settings/device policy */ - val isFaceAuthenticationEnabled: Flow<Boolean> + val isFingerprintEnrolledAndEnabled: StateFlow<Boolean> /** - * Whether the current user is allowed to use a strong biometric for device entry based on - * Android Security policies. If false, the user may be able to use primary authentication for - * device entry. + * If the current user can enter the device using fingerprint, right now. + * + * This returns true if there are no strong auth flags that restrict the user from using + * fingerprint and [isFingerprintEnrolledAndEnabled] is true */ - val isStrongBiometricAllowed: StateFlow<Boolean> + val isFingerprintAuthCurrentlyAllowed: StateFlow<Boolean> /** - * Whether the current user is allowed to use a convenience biometric for device entry based on - * Android Security policies. If false, the user may be able to use strong biometric or primary - * authentication for device entry. + * If the current user can use face auth to enter the device. This is true when the user has + * face auth enrolled, and is enabled in settings/device policy. */ - val isNonStrongBiometricAllowed: StateFlow<Boolean> + val isFaceAuthEnrolledAndEnabled: Flow<Boolean> - /** Whether fingerprint feature is enabled for the current user by the DevicePolicy */ - val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> + /** + * If the current user can use face auth to enter the device right now. This is true when + * [isFaceAuthEnrolledAndEnabled] is true and strong auth settings allow face auth to run and + * face auth is supported by the current device posture. + */ + val isFaceAuthCurrentlyAllowed: Flow<Boolean> /** * Whether face authentication is supported for the current device posture. Face auth can be @@ -130,6 +131,8 @@ constructor( @Background backgroundDispatcher: CoroutineDispatcher, biometricManager: BiometricManager?, devicePostureRepository: DevicePostureRepository, + facePropertyRepository: FacePropertyRepository, + fingerprintPropertyRepository: FingerprintPropertyRepository, dumpManager: DumpManager, ) : BiometricSettingsRepository, Dumpable { @@ -165,7 +168,9 @@ constructor( } override fun dump(pw: PrintWriter, args: Array<String?>) { - pw.println("isFingerprintEnrolled=${isFingerprintEnrolled.value}") + pw.println("isFingerprintEnrolledAndEnabled=${isFingerprintEnrolledAndEnabled.value}") + pw.println("isFingerprintAuthCurrentlyAllowed=${isFingerprintAuthCurrentlyAllowed.value}") + pw.println("isNonStrongBiometricAllowed=${isNonStrongBiometricAllowed.value}") pw.println("isStrongBiometricAllowed=${isStrongBiometricAllowed.value}") pw.println("isFingerprintEnabledByDevicePolicy=${isFingerprintEnabledByDevicePolicy.value}") } @@ -180,7 +185,7 @@ constructor( user = UserHandle.ALL ) - override val isFingerprintEnrolled: StateFlow<Boolean> = + private val isFingerprintEnrolled: Flow<Boolean> = selectedUserId .flatMapLatest { currentUserId -> conflatedCallbackFlow { @@ -211,7 +216,7 @@ constructor( authController.isFingerprintEnrolled(userRepository.getSelectedUserInfo().id) ) - override val isFaceEnrolled: Flow<Boolean> = + private val isFaceEnrolled: Flow<Boolean> = selectedUserId.flatMapLatest { selectedUserId: Int -> conflatedCallbackFlow { val callback = @@ -245,14 +250,6 @@ constructor( isFaceEnabledByBiometricsManager.map { biometricsEnabledForUser[userInfo.id] ?: false } } - override val isFaceAuthenticationEnabled: Flow<Boolean> - get() = - combine(isFaceEnabledByBiometricsManagerForCurrentUser, isFaceEnabledByDevicePolicy) { - biometricsManagerSetting, - devicePolicySetting -> - biometricsManagerSetting && devicePolicySetting - } - private val isFaceEnabledByDevicePolicy: Flow<Boolean> = combine(selectedUserId, devicePolicyChangedForAllUsers) { userId, _ -> devicePolicyManager.isFaceDisabled(userId) @@ -263,6 +260,13 @@ constructor( .flowOn(backgroundDispatcher) .distinctUntilChanged() + private val isFaceAuthenticationEnabled: Flow<Boolean> = + combine(isFaceEnabledByBiometricsManagerForCurrentUser, isFaceEnabledByDevicePolicy) { + biometricsManagerSetting, + devicePolicySetting -> + biometricsManagerSetting && devicePolicySetting + } + private val isFaceEnabledByBiometricsManager: Flow<Pair<Int, Boolean>> = conflatedCallbackFlow { val callback = @@ -283,7 +287,7 @@ constructor( // being registered. .stateIn(scope, SharingStarted.Eagerly, Pair(0, false)) - override val isStrongBiometricAllowed: StateFlow<Boolean> = + private val isStrongBiometricAllowed: StateFlow<Boolean> = strongAuthTracker.isStrongBiometricAllowed.stateIn( scope, SharingStarted.Eagerly, @@ -293,7 +297,7 @@ constructor( ) ) - override val isNonStrongBiometricAllowed: StateFlow<Boolean> = + private val isNonStrongBiometricAllowed: StateFlow<Boolean> = strongAuthTracker.isNonStrongBiometricAllowed.stateIn( scope, SharingStarted.Eagerly, @@ -303,7 +307,19 @@ constructor( ) ) - override val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> = + private val isFingerprintBiometricAllowed: Flow<Boolean> = + fingerprintPropertyRepository.strength.flatMapLatest { + if (it == SensorStrength.STRONG) isStrongBiometricAllowed + else isNonStrongBiometricAllowed + } + + private val isFaceBiometricsAllowed: Flow<Boolean> = + facePropertyRepository.sensorInfo.flatMapLatest { + if (it?.strength == SensorStrength.STRONG) isStrongBiometricAllowed + else isNonStrongBiometricAllowed + } + + private val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> = selectedUserId .flatMapLatest { userId -> devicePolicyChangedForAllUsers @@ -319,6 +335,25 @@ constructor( userRepository.getSelectedUserInfo().id ) ) + + override val isFingerprintEnrolledAndEnabled: StateFlow<Boolean> = + isFingerprintEnrolled + .and(isFingerprintEnabledByDevicePolicy) + .stateIn(scope, SharingStarted.Eagerly, false) + + override val isFingerprintAuthCurrentlyAllowed: StateFlow<Boolean> = + isFingerprintEnrolledAndEnabled + .and(isFingerprintBiometricAllowed) + .stateIn(scope, SharingStarted.Eagerly, false) + + override val isFaceAuthEnrolledAndEnabled: Flow<Boolean> + get() = isFaceAuthenticationEnabled.and(isFaceEnrolled) + + override val isFaceAuthCurrentlyAllowed: Flow<Boolean> + get() = + isFaceAuthEnrolledAndEnabled + .and(isFaceBiometricsAllowed) + .and(isFaceAuthSupportedInCurrentPosture) } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt index 689414711388..93eb103d74e1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt @@ -26,8 +26,6 @@ import com.android.internal.logging.UiEventLogger import com.android.keyguard.FaceAuthUiEvent import com.android.systemui.Dumpable import com.android.systemui.R -import com.android.systemui.biometrics.data.repository.FacePropertyRepository -import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow @@ -72,7 +70,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map @@ -162,7 +159,6 @@ constructor( @FaceAuthTableLog private val faceAuthLog: TableLogBuffer, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val featureFlags: FeatureFlags, - facePropertyRepository: FacePropertyRepository, dumpManager: DumpManager, ) : DeviceEntryFaceAuthRepository, Dumpable { private var authCancellationSignal: CancellationSignal? = null @@ -182,13 +178,6 @@ constructor( override val detectionStatus: Flow<FaceDetectionStatus> get() = _detectionStatus.filterNotNull() - private val isFaceBiometricsAllowed: Flow<Boolean> = - facePropertyRepository.sensorInfo.flatMapLatest { - if (it?.strength == SensorStrength.STRONG) - biometricSettingsRepository.isStrongBiometricAllowed - else biometricSettingsRepository.isNonStrongBiometricAllowed - } - private val _isLockedOut = MutableStateFlow(false) override val isLockedOut: StateFlow<Boolean> = _isLockedOut @@ -313,8 +302,10 @@ constructor( canFaceAuthOrDetectRun(faceDetectLog), logAndObserve(isBypassEnabled, "isBypassEnabled", faceDetectLog), logAndObserve( - isFaceBiometricsAllowed.isFalse().or(trustRepository.isCurrentUserTrusted), - "biometricIsNotAllowedOrCurrentUserIsTrusted", + biometricSettingsRepository.isFaceAuthCurrentlyAllowed + .isFalse() + .or(trustRepository.isCurrentUserTrusted), + "faceAuthIsNotCurrentlyAllowedOrCurrentUserIsTrusted", faceDetectLog ), // We don't want to run face detect if fingerprint can be used to unlock the device @@ -346,13 +337,8 @@ constructor( private fun canFaceAuthOrDetectRun(tableLogBuffer: TableLogBuffer): Flow<Boolean> { return listOf( logAndObserve( - biometricSettingsRepository.isFaceEnrolled, - "isFaceEnrolled", - tableLogBuffer - ), - logAndObserve( - biometricSettingsRepository.isFaceAuthenticationEnabled, - "isFaceAuthenticationEnabled", + biometricSettingsRepository.isFaceAuthEnrolledAndEnabled, + "isFaceAuthEnrolledAndEnabled", tableLogBuffer ), logAndObserve(faceAuthPaused.isFalse(), "faceAuthIsNotPaused", tableLogBuffer), @@ -406,7 +392,11 @@ constructor( "currentUserIsNotTrusted", faceAuthLog ), - logAndObserve(isFaceBiometricsAllowed, "isFaceBiometricsAllowed", faceAuthLog), + logAndObserve( + biometricSettingsRepository.isFaceAuthCurrentlyAllowed, + "isFaceAuthCurrentlyAllowed", + faceAuthLog + ), logAndObserve(isAuthenticated.isFalse(), "faceNotAuthenticated", faceAuthLog), ) .reduce(::and) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt index efae3fe1af2c..8eb274a65bc4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt @@ -24,13 +24,12 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Pattern import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT -import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.shared.model.BouncerMessageModel +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.util.mockito.whenever import com.google.common.truth.StringSubject import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Before @@ -39,13 +38,12 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations -@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class BouncerMessageFactoryTest : SysuiTestCase() { private lateinit var underTest: BouncerMessageFactory - @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository @Mock private lateinit var securityModel: KeyguardSecurityModel @@ -55,7 +53,8 @@ class BouncerMessageFactoryTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) testScope = TestScope() - underTest = BouncerMessageFactory(updateMonitor, securityModel) + biometricSettingsRepository = FakeBiometricSettingsRepository() + underTest = BouncerMessageFactory(biometricSettingsRepository, securityModel) } @Test @@ -167,7 +166,7 @@ class BouncerMessageFactoryTest : SysuiTestCase() { secondaryMessageOverride: String? = null, ): BouncerMessageModel? { whenever(securityModel.getSecurityMode(0)).thenReturn(mode) - whenever(updateMonitor.isUnlockingWithFingerprintAllowed).thenReturn(fpAuthAllowed) + biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(fpAuthAllowed) return underTest.createFromPromptReason( reason, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt index 2be7d8a43a18..562a8ef512d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt @@ -101,14 +101,15 @@ class BouncerMessageRepositoryTest : SysuiTestCase() { fingerprintRepository = FakeDeviceEntryFingerprintAuthRepository() testScope = TestScope() - whenever(updateMonitor.isUnlockingWithFingerprintAllowed).thenReturn(false) + biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false) whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN) underTest = BouncerMessageRepositoryImpl( trustRepository = trustRepository, biometricSettingsRepository = biometricSettingsRepository, updateMonitor = updateMonitor, - bouncerMessageFactory = BouncerMessageFactory(updateMonitor, securityModel), + bouncerMessageFactory = + BouncerMessageFactory(biometricSettingsRepository, securityModel), userRepository = userRepository, fingerprintAuthRepository = fingerprintRepository, systemPropertiesHelper = systemPropertiesHelper @@ -222,8 +223,7 @@ class BouncerMessageRepositoryTest : SysuiTestCase() { whenever(systemPropertiesHelper.get("sys.boot.reason.last")) .thenReturn("reboot,mainline_update") userRepository.setSelectedUserInfo(PRIMARY_USER) - biometricSettingsRepository.setFaceEnrolled(true) - biometricSettingsRepository.setIsFaceAuthEnabled(true) + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) verifyMessagesForAuthFlag( STRONG_AUTH_REQUIRED_AFTER_BOOT to @@ -236,8 +236,8 @@ class BouncerMessageRepositoryTest : SysuiTestCase() { testScope.runTest { userRepository.setSelectedUserInfo(PRIMARY_USER) trustRepository.setCurrentUserTrustManaged(false) - biometricSettingsRepository.setFaceEnrolled(false) - biometricSettingsRepository.setFingerprintEnrolled(false) + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) verifyMessagesForAuthFlag( STRONG_AUTH_NOT_REQUIRED to null, @@ -258,8 +258,8 @@ class BouncerMessageRepositoryTest : SysuiTestCase() { fun authFlagsChanges_withTrustManaged_providesDifferentMessages() = testScope.runTest { userRepository.setSelectedUserInfo(PRIMARY_USER) - biometricSettingsRepository.setFaceEnrolled(false) - biometricSettingsRepository.setFingerprintEnrolled(false) + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) trustRepository.setCurrentUserTrustManaged(true) @@ -290,10 +290,9 @@ class BouncerMessageRepositoryTest : SysuiTestCase() { testScope.runTest { userRepository.setSelectedUserInfo(PRIMARY_USER) trustRepository.setCurrentUserTrustManaged(false) - biometricSettingsRepository.setFingerprintEnrolled(false) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) - biometricSettingsRepository.setIsFaceAuthEnabled(true) - biometricSettingsRepository.setFaceEnrolled(true) + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) verifyMessagesForAuthFlag( STRONG_AUTH_NOT_REQUIRED to null, @@ -320,11 +319,9 @@ class BouncerMessageRepositoryTest : SysuiTestCase() { testScope.runTest { userRepository.setSelectedUserInfo(PRIMARY_USER) trustRepository.setCurrentUserTrustManaged(false) - biometricSettingsRepository.setIsFaceAuthEnabled(false) - biometricSettingsRepository.setFaceEnrolled(false) + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) - biometricSettingsRepository.setFingerprintEnrolled(true) - biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) verifyMessagesForAuthFlag( STRONG_AUTH_NOT_REQUIRED to null, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt index 38e5728f6e70..0d172706b127 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt @@ -94,9 +94,9 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { } @Test - fun canShowAlternateBouncerForFingerprint_noFingerprintsEnrolled() { + fun canShowAlternateBouncerForFingerprint_ifFingerprintIsNotUsuallyAllowed() { givenCanShowAlternateBouncer() - biometricSettingsRepository.setFingerprintEnrolled(false) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) assertFalse(underTest.canShowAlternateBouncerForFingerprint()) } @@ -104,15 +104,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { @Test fun canShowAlternateBouncerForFingerprint_strongBiometricNotAllowed() { givenCanShowAlternateBouncer() - biometricSettingsRepository.setStrongBiometricAllowed(false) - - assertFalse(underTest.canShowAlternateBouncerForFingerprint()) - } - - @Test - fun canShowAlternateBouncerForFingerprint_devicePolicyDoesNotAllowFingerprint() { - givenCanShowAlternateBouncer() - biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(false) + biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false) assertFalse(underTest.canShowAlternateBouncerForFingerprint()) } @@ -189,14 +181,13 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { private fun givenCanShowAlternateBouncer() { bouncerRepository.setAlternateBouncerUIAvailable(true) - biometricSettingsRepository.setFingerprintEnrolled(true) - biometricSettingsRepository.setStrongBiometricAllowed(true) - biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) + biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true) whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false) whenever(keyguardStateController.isUnlocked).thenReturn(false) } private fun givenCannotShowAlternateBouncer() { - biometricSettingsRepository.setFingerprintEnrolled(false) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt index 3ca94aa8e7af..4089abef399b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt @@ -22,9 +22,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN -import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.R.string.keyguard_enter_pin import com.android.systemui.R.string.kg_too_many_failed_attempts_countdown +import com.android.systemui.R.string.kg_unlock_with_pin_or_fp import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.factory.BouncerMessageFactory import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository @@ -34,11 +33,11 @@ import com.android.systemui.coroutines.FlowValue import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.KotlinArgumentCaptor import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Before @@ -49,14 +48,13 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidJUnit4::class) class BouncerMessageInteractorTest : SysuiTestCase() { @Mock private lateinit var securityModel: KeyguardSecurityModel - @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository @Mock private lateinit var countDownTimerUtil: CountDownTimerUtil private lateinit var countDownTimerCallback: KotlinArgumentCaptor<CountDownTimerCallback> private lateinit var underTest: BouncerMessageInteractor @@ -73,10 +71,11 @@ class BouncerMessageInteractorTest : SysuiTestCase() { userRepository.setUserInfos(listOf(PRIMARY_USER)) testScope = TestScope() countDownTimerCallback = KotlinArgumentCaptor(CountDownTimerCallback::class.java) + biometricSettingsRepository = FakeBiometricSettingsRepository() allowTestableLooperAsMainThread() whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN) - whenever(updateMonitor.isUnlockingWithFingerprintAllowed).thenReturn(false) + biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true) } suspend fun TestScope.init() { @@ -86,7 +85,7 @@ class BouncerMessageInteractorTest : SysuiTestCase() { underTest = BouncerMessageInteractor( repository = repository, - factory = BouncerMessageFactory(updateMonitor, securityModel), + factory = BouncerMessageFactory(biometricSettingsRepository, securityModel), userRepository = userRepository, countDownTimerUtil = countDownTimerUtil, featureFlags = featureFlags @@ -151,7 +150,8 @@ class BouncerMessageInteractorTest : SysuiTestCase() { underTest.setCustomMessage("not empty") val customMessage = repository.customMessage - assertThat(customMessage.value!!.message!!.messageResId).isEqualTo(keyguard_enter_pin) + assertThat(customMessage.value!!.message!!.messageResId) + .isEqualTo(kg_unlock_with_pin_or_fp) assertThat(customMessage.value!!.secondaryMessage!!.message).isEqualTo("not empty") underTest.setCustomMessage(null) @@ -168,7 +168,7 @@ class BouncerMessageInteractorTest : SysuiTestCase() { val faceAcquisitionMessage = repository.faceAcquisitionMessage assertThat(faceAcquisitionMessage.value!!.message!!.messageResId) - .isEqualTo(keyguard_enter_pin) + .isEqualTo(kg_unlock_with_pin_or_fp) assertThat(faceAcquisitionMessage.value!!.secondaryMessage!!.message) .isEqualTo("not empty") @@ -186,7 +186,7 @@ class BouncerMessageInteractorTest : SysuiTestCase() { val fingerprintAcquisitionMessage = repository.fingerprintAcquisitionMessage assertThat(fingerprintAcquisitionMessage.value!!.message!!.messageResId) - .isEqualTo(keyguard_enter_pin) + .isEqualTo(kg_unlock_with_pin_or_fp) assertThat(fingerprintAcquisitionMessage.value!!.secondaryMessage!!.message) .isEqualTo("not empty") @@ -275,7 +275,8 @@ class BouncerMessageInteractorTest : SysuiTestCase() { repository.setBiometricLockedOutMessage(null) // sets the default message if everything else is null - assertThat(bouncerMessage()!!.message!!.messageResId).isEqualTo(keyguard_enter_pin) + assertThat(bouncerMessage()!!.message!!.messageResId) + .isEqualTo(kg_unlock_with_pin_or_fp) } private fun message(value: String): BouncerMessageModel { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt index c6a2fa50b446..a6930d595326 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt @@ -36,6 +36,11 @@ import com.android.systemui.R import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController +import com.android.systemui.biometrics.data.repository.FaceSensorInfo +import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository +import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository +import com.android.systemui.biometrics.shared.model.FingerprintSensorType +import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.BiometricType.FACE @@ -63,6 +68,7 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.isNull import org.mockito.Captor import org.mockito.Mock +import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.times import org.mockito.Mockito.verify @@ -89,6 +95,8 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { ArgumentCaptor<IBiometricEnabledOnKeyguardCallback.Stub> private lateinit var userRepository: FakeUserRepository private lateinit var devicePostureRepository: FakeDevicePostureRepository + private lateinit var facePropertyRepository: FakeFacePropertyRepository + private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository private lateinit var testDispatcher: TestDispatcher private lateinit var testScope: TestScope @@ -102,6 +110,8 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { testScope = TestScope(testDispatcher) userRepository = FakeUserRepository() devicePostureRepository = FakeDevicePostureRepository() + facePropertyRepository = FakeFacePropertyRepository() + fingerprintPropertyRepository = FakeFingerprintPropertyRepository() } private suspend fun createBiometricSettingsRepository() { @@ -120,74 +130,110 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { biometricManager = biometricManager, devicePostureRepository = devicePostureRepository, dumpManager = dumpManager, + facePropertyRepository = facePropertyRepository, + fingerprintPropertyRepository = fingerprintPropertyRepository, ) testScope.runCurrent() + fingerprintPropertyRepository.setProperties( + 1, + SensorStrength.STRONG, + FingerprintSensorType.UDFPS_OPTICAL, + emptyMap() + ) verify(lockPatternUtils).registerStrongAuthTracker(strongAuthTracker.capture()) + verify(authController, atLeastOnce()).addCallback(authControllerCallback.capture()) } @Test fun fingerprintEnrollmentChange() = testScope.runTest { createBiometricSettingsRepository() - val fingerprintEnrolled = collectLastValue(underTest.isFingerprintEnrolled) + val fingerprintAllowed = collectLastValue(underTest.isFingerprintEnrolledAndEnabled) runCurrent() - verify(authController).addCallback(authControllerCallback.capture()) whenever(authController.isFingerprintEnrolled(anyInt())).thenReturn(true) enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true) - assertThat(fingerprintEnrolled()).isTrue() + assertThat(fingerprintAllowed()).isTrue() whenever(authController.isFingerprintEnrolled(anyInt())).thenReturn(false) enrollmentChange(UNDER_DISPLAY_FINGERPRINT, ANOTHER_USER_ID, false) - assertThat(fingerprintEnrolled()).isTrue() + assertThat(fingerprintAllowed()).isTrue() enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, false) - assertThat(fingerprintEnrolled()).isFalse() + assertThat(fingerprintAllowed()).isFalse() } @Test fun strongBiometricAllowedChange() = testScope.runTest { + fingerprintIsEnrolled() + doNotDisableKeyguardAuthFeatures() createBiometricSettingsRepository() - val strongBiometricAllowed = collectLastValue(underTest.isStrongBiometricAllowed) + + val strongBiometricAllowed by + collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed) runCurrent() onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) - assertThat(strongBiometricAllowed()).isTrue() + assertThat(strongBiometricAllowed).isTrue() onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_BOOT, PRIMARY_USER_ID) - assertThat(strongBiometricAllowed()).isFalse() + assertThat(strongBiometricAllowed).isFalse() } @Test fun convenienceBiometricAllowedChange() = testScope.runTest { overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, false) + deviceIsInPostureThatSupportsFaceAuth() + faceAuthIsEnrolled() + faceAuthIsNonStrongBiometric() createBiometricSettingsRepository() - val convenienceBiometricAllowed = - collectLastValue(underTest.isNonStrongBiometricAllowed) - runCurrent() + val convenienceFaceAuthAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed) + doNotDisableKeyguardAuthFeatures() + faceAuthIsEnabledByBiometricManager() + + onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) onNonStrongAuthChanged(true, PRIMARY_USER_ID) - assertThat(convenienceBiometricAllowed()).isTrue() + runCurrent() + assertThat(convenienceFaceAuthAllowed).isTrue() onNonStrongAuthChanged(false, ANOTHER_USER_ID) - assertThat(convenienceBiometricAllowed()).isTrue() + assertThat(convenienceFaceAuthAllowed).isTrue() onNonStrongAuthChanged(false, PRIMARY_USER_ID) - assertThat(convenienceBiometricAllowed()).isFalse() + assertThat(convenienceFaceAuthAllowed).isFalse() mContext.orCreateTestableResources.removeOverride( com.android.internal.R.bool.config_strongAuthRequiredOnBoot ) } + private fun faceAuthIsNonStrongBiometric() { + facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.CONVENIENCE)) + } + + private fun faceAuthIsStrongBiometric() { + facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG)) + } + + private fun deviceIsInPostureThatSupportsFaceAuth() { + overrideResource( + R.integer.config_face_auth_supported_posture, + DevicePostureController.DEVICE_POSTURE_FLIPPED + ) + devicePostureRepository.setCurrentPosture(DevicePosture.FLIPPED) + } + @Test fun whenStrongAuthRequiredAfterBoot_nonStrongBiometricNotAllowed() = testScope.runTest { overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, true) createBiometricSettingsRepository() + faceAuthIsNonStrongBiometric() + faceAuthIsEnrolled() + doNotDisableKeyguardAuthFeatures() - val convenienceBiometricAllowed = - collectLastValue(underTest.isNonStrongBiometricAllowed) + val convenienceBiometricAllowed = collectLastValue(underTest.isFaceAuthCurrentlyAllowed) runCurrent() onNonStrongAuthChanged(true, PRIMARY_USER_ID) @@ -201,16 +247,24 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { fun whenStrongBiometricAuthIsNotAllowed_nonStrongBiometrics_alsoNotAllowed() = testScope.runTest { overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, false) + faceAuthIsNonStrongBiometric() + deviceIsInPostureThatSupportsFaceAuth() + faceAuthIsEnrolled() createBiometricSettingsRepository() - - val convenienceBiometricAllowed = - collectLastValue(underTest.isNonStrongBiometricAllowed) + doNotDisableKeyguardAuthFeatures() + faceAuthIsEnabledByBiometricManager() runCurrent() + + val convenienceBiometricAllowed by + collectLastValue(underTest.isFaceAuthCurrentlyAllowed) + + onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) onNonStrongAuthChanged(true, PRIMARY_USER_ID) - assertThat(convenienceBiometricAllowed()).isTrue() + runCurrent() + assertThat(convenienceBiometricAllowed).isTrue() onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, PRIMARY_USER_ID) - assertThat(convenienceBiometricAllowed()).isFalse() + assertThat(convenienceBiometricAllowed).isFalse() mContext.orCreateTestableResources.removeOverride( com.android.internal.R.bool.config_strongAuthRequiredOnBoot ) @@ -229,9 +283,11 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { @Test fun fingerprintDisabledByDpmChange() = testScope.runTest { + fingerprintIsEnrolled(PRIMARY_USER_ID) createBiometricSettingsRepository() + val fingerprintEnabledByDevicePolicy = - collectLastValue(underTest.isFingerprintEnabledByDevicePolicy) + collectLastValue(underTest.isFingerprintEnrolledAndEnabled) runCurrent() whenever(devicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt())) @@ -244,43 +300,57 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { assertThat(fingerprintEnabledByDevicePolicy()).isTrue() } + private fun fingerprintIsEnrolled(userId: Int = PRIMARY_USER_ID) { + whenever(authController.isFingerprintEnrolled(userId)).thenReturn(true) + } + @Test fun faceEnrollmentChangeIsPropagatedForTheCurrentUser() = testScope.runTest { createBiometricSettingsRepository() + faceAuthIsEnabledByBiometricManager() + + doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID) + runCurrent() clearInvocations(authController) whenever(authController.isFaceAuthEnrolled(PRIMARY_USER_ID)).thenReturn(false) - val faceEnrolled = collectLastValue(underTest.isFaceEnrolled) + val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) - assertThat(faceEnrolled()).isFalse() + assertThat(faceAuthAllowed()).isFalse() verify(authController).addCallback(authControllerCallback.capture()) enrollmentChange(REAR_FINGERPRINT, PRIMARY_USER_ID, true) - assertThat(faceEnrolled()).isFalse() + assertThat(faceAuthAllowed()).isFalse() enrollmentChange(SIDE_FINGERPRINT, PRIMARY_USER_ID, true) - assertThat(faceEnrolled()).isFalse() + assertThat(faceAuthAllowed()).isFalse() enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true) - assertThat(faceEnrolled()).isFalse() + assertThat(faceAuthAllowed()).isFalse() whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true) enrollmentChange(FACE, ANOTHER_USER_ID, true) - assertThat(faceEnrolled()).isFalse() + assertThat(faceAuthAllowed()).isFalse() - whenever(authController.isFaceAuthEnrolled(PRIMARY_USER_ID)).thenReturn(true) + faceAuthIsEnrolled() enrollmentChange(FACE, PRIMARY_USER_ID, true) - assertThat(faceEnrolled()).isTrue() + assertThat(faceAuthAllowed()).isTrue() } + private fun faceAuthIsEnabledByBiometricManager(userId: Int = PRIMARY_USER_ID) { + verify(biometricManager, atLeastOnce()) + .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture()) + biometricManagerCallback.value.onChanged(true, userId) + } + @Test fun faceEnrollmentStatusOfNewUserUponUserSwitch() = testScope.runTest { @@ -290,21 +360,26 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { whenever(authController.isFaceAuthEnrolled(PRIMARY_USER_ID)).thenReturn(false) whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true) - val faceEnrolled = collectLastValue(underTest.isFaceEnrolled) + val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) - assertThat(faceEnrolled()).isFalse() + assertThat(faceAuthAllowed()).isFalse() } @Test fun faceEnrollmentChangesArePropagatedAfterUserSwitch() = testScope.runTest { createBiometricSettingsRepository() + verify(biometricManager) + .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture()) userRepository.setSelectedUserInfo(ANOTHER_USER) + doNotDisableKeyguardAuthFeatures(ANOTHER_USER_ID) + biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID) + runCurrent() clearInvocations(authController) - val faceEnrolled = collectLastValue(underTest.isFaceEnrolled) + val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) runCurrent() verify(authController).addCallback(authControllerCallback.capture()) @@ -312,12 +387,14 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true) enrollmentChange(FACE, ANOTHER_USER_ID, true) - assertThat(faceEnrolled()).isTrue() + assertThat(faceAuthAllowed()).isTrue() } @Test fun devicePolicyControlsFaceAuthenticationEnabledState() = testScope.runTest { + faceAuthIsEnrolled() + createBiometricSettingsRepository() verify(biometricManager) .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture()) @@ -325,62 +402,70 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID))) .thenReturn(KEYGUARD_DISABLE_FINGERPRINT or KEYGUARD_DISABLE_FACE) - val isFaceAuthEnabled = collectLastValue(underTest.isFaceAuthenticationEnabled) + val isFaceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) runCurrent() broadcastDPMStateChange() - assertThat(isFaceAuthEnabled()).isFalse() + assertThat(isFaceAuthAllowed()).isFalse() biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID) runCurrent() - assertThat(isFaceAuthEnabled()).isFalse() + assertThat(isFaceAuthAllowed()).isFalse() whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID))) .thenReturn(KEYGUARD_DISABLE_FINGERPRINT) broadcastDPMStateChange() - assertThat(isFaceAuthEnabled()).isTrue() + assertThat(isFaceAuthAllowed()).isTrue() } @Test fun biometricManagerControlsFaceAuthenticationEnabledStatus() = testScope.runTest { + faceAuthIsEnrolled() + createBiometricSettingsRepository() verify(biometricManager) .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture()) whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID))) .thenReturn(0) broadcastDPMStateChange() - val isFaceAuthEnabled = collectLastValue(underTest.isFaceAuthenticationEnabled) + val isFaceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) - assertThat(isFaceAuthEnabled()).isFalse() + assertThat(isFaceAuthAllowed()).isFalse() // Value changes for another user biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID) - assertThat(isFaceAuthEnabled()).isFalse() + assertThat(isFaceAuthAllowed()).isFalse() // Value changes for current user. biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID) - assertThat(isFaceAuthEnabled()).isTrue() + assertThat(isFaceAuthAllowed()).isTrue() } + private fun faceAuthIsEnrolled(userId: Int = PRIMARY_USER_ID) { + whenever(authController.isFaceAuthEnrolled(userId)).thenReturn(true) + } + @Test fun userChange_biometricEnabledChange_handlesRaceCondition() = testScope.runTest { createBiometricSettingsRepository() + whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true) + verify(biometricManager) .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture()) - val isFaceAuthEnabled = collectLastValue(underTest.isFaceAuthenticationEnabled) + val isFaceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID) runCurrent() userRepository.setSelectedUserInfo(ANOTHER_USER) runCurrent() - assertThat(isFaceAuthEnabled()).isTrue() + assertThat(isFaceAuthAllowed()).isTrue() } @Test @@ -388,9 +473,9 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { testScope.runTest { createBiometricSettingsRepository() - collectLastValue(underTest.isFaceAuthenticationEnabled)() - collectLastValue(underTest.isFaceAuthenticationEnabled)() - collectLastValue(underTest.isFaceAuthenticationEnabled)() + collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)() + collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)() + collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)() verify(biometricManager, times(1)).registerEnabledOnKeyguardCallback(any()) } @@ -495,10 +580,138 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { assertThat(authFlags()!!.flag).isEqualTo(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) } + @Test + fun faceAuthCurrentlyAllowed_dependsOnStrongAuthBiometricSetting_ifFaceIsClass3() = + testScope.runTest { + createBiometricSettingsRepository() + val isFaceAuthCurrentlyAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed) + + faceAuthIsEnrolled() + deviceIsInPostureThatSupportsFaceAuth() + doNotDisableKeyguardAuthFeatures() + faceAuthIsStrongBiometric() + faceAuthIsEnabledByBiometricManager() + + onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) + onNonStrongAuthChanged(false, PRIMARY_USER_ID) + + assertThat(isFaceAuthCurrentlyAllowed).isTrue() + + onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, PRIMARY_USER_ID) + onNonStrongAuthChanged(true, PRIMARY_USER_ID) + + assertThat(isFaceAuthCurrentlyAllowed).isFalse() + } + + @Test + fun faceAuthCurrentlyAllowed_dependsOnNonStrongAuthBiometricSetting_ifFaceIsNotStrong() = + testScope.runTest { + createBiometricSettingsRepository() + val isFaceAuthCurrentlyAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed) + + faceAuthIsEnrolled() + deviceIsInPostureThatSupportsFaceAuth() + doNotDisableKeyguardAuthFeatures() + faceAuthIsNonStrongBiometric() + faceAuthIsEnabledByBiometricManager() + + onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) + onNonStrongAuthChanged(false, PRIMARY_USER_ID) + + assertThat(isFaceAuthCurrentlyAllowed).isFalse() + + onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, PRIMARY_USER_ID) + onNonStrongAuthChanged(true, PRIMARY_USER_ID) + + assertThat(isFaceAuthCurrentlyAllowed).isFalse() + + onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) + onNonStrongAuthChanged(true, PRIMARY_USER_ID) + + assertThat(isFaceAuthCurrentlyAllowed).isTrue() + } + + @Test + fun fpAuthCurrentlyAllowed_dependsOnNonStrongAuthBiometricSetting_ifFpIsNotStrong() = + testScope.runTest { + createBiometricSettingsRepository() + val isFingerprintCurrentlyAllowed by + collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed) + + fingerprintIsEnrolled(PRIMARY_USER_ID) + enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true) + doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID) + runCurrent() + + fingerprintPropertyRepository.setProperties( + 1, + SensorStrength.STRONG, + FingerprintSensorType.UDFPS_OPTICAL, + emptyMap() + ) + // Non strong auth is not allowed now, FP is marked strong + onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) + onNonStrongAuthChanged(false, PRIMARY_USER_ID) + + assertThat(isFingerprintCurrentlyAllowed).isTrue() + + fingerprintPropertyRepository.setProperties( + 1, + SensorStrength.CONVENIENCE, + FingerprintSensorType.UDFPS_OPTICAL, + emptyMap() + ) + assertThat(isFingerprintCurrentlyAllowed).isFalse() + + fingerprintPropertyRepository.setProperties( + 1, + SensorStrength.WEAK, + FingerprintSensorType.UDFPS_OPTICAL, + emptyMap() + ) + assertThat(isFingerprintCurrentlyAllowed).isFalse() + } + + @Test + fun fpAuthCurrentlyAllowed_dependsOnStrongAuthBiometricSetting_ifFpIsStrong() = + testScope.runTest { + createBiometricSettingsRepository() + val isFingerprintCurrentlyAllowed by + collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed) + + fingerprintIsEnrolled(PRIMARY_USER_ID) + enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true) + doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID) + runCurrent() + + fingerprintPropertyRepository.setProperties( + 1, + SensorStrength.STRONG, + FingerprintSensorType.UDFPS_OPTICAL, + emptyMap() + ) + // Non strong auth is not allowed now, FP is marked strong + onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, PRIMARY_USER_ID) + onNonStrongAuthChanged(true, PRIMARY_USER_ID) + + assertThat(isFingerprintCurrentlyAllowed).isFalse() + + onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) + onNonStrongAuthChanged(false, PRIMARY_USER_ID) + + assertThat(isFingerprintCurrentlyAllowed).isTrue() + } + private fun enrollmentChange(biometricType: BiometricType, userId: Int, enabled: Boolean) { authControllerCallback.value.onEnrollmentsChanged(biometricType, userId, enabled) } + private fun doNotDisableKeyguardAuthFeatures(userId: Int = PRIMARY_USER_ID) { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(userId))) + .thenReturn(0) + broadcastDPMStateChange() + } + private fun broadcastDPMStateChange() { fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( context, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt index fe5b8120428d..64b94707da57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt @@ -40,9 +40,7 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.R import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.data.repository.FaceSensorInfo import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository -import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.coroutines.FlowValue @@ -255,7 +253,6 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { faceAuthBuffer, keyguardTransitionInteractor, featureFlags, - fakeFacePropertyRepository, dumpManager, ) } @@ -525,15 +522,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test - fun authenticateDoesNotRunIfFaceIsNotEnrolled() = + fun authenticateDoesNotRunIfFaceIsNotUsuallyAllowed() = testScope.runTest { - testGatingCheckForFaceAuth { biometricSettingsRepository.setFaceEnrolled(false) } - } - - @Test - fun authenticateDoesNotRunIfFaceIsNotEnabled() = - testScope.runTest { - testGatingCheckForFaceAuth { biometricSettingsRepository.setIsFaceAuthEnabled(false) } + testGatingCheckForFaceAuth { + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) + } } @Test @@ -589,21 +582,10 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test - fun authenticateDoesNotRunWhenNonStrongBiometricIsNotAllowed() = + fun authenticateDoesNotRunWhenFaceAuthIsNotCurrentlyAllowedToRun() = testScope.runTest { testGatingCheckForFaceAuth { - biometricSettingsRepository.setIsNonStrongBiometricAllowed(false) - } - } - - @Test - fun authenticateDoesNotRunWhenStrongBiometricIsNotAllowedAndFaceSensorIsStrong() = - testScope.runTest { - fakeFacePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG)) - runCurrent() - - testGatingCheckForFaceAuth(isFaceStrong = true) { - biometricSettingsRepository.setIsStrongBiometricAllowed(false) + biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false) } } @@ -662,7 +644,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { allPreconditionsToRunFaceAuthAreTrue() // Flip one precondition to false. - biometricSettingsRepository.setIsNonStrongBiometricAllowed(false) + biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false) assertThat(canFaceAuthRun()).isFalse() underTest.authenticate( FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, @@ -827,15 +809,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test - fun detectDoesNotRunWhenFaceIsNotEnrolled() = - testScope.runTest { - testGatingCheckForDetect { biometricSettingsRepository.setFaceEnrolled(false) } - } - - @Test - fun detectDoesNotRunWhenFaceIsNotEnabled() = + fun detectDoesNotRunWhenFaceIsNotUsuallyAllowed() = testScope.runTest { - testGatingCheckForDetect { biometricSettingsRepository.setIsFaceAuthEnabled(false) } + testGatingCheckForDetect { + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) + } } @Test @@ -932,23 +910,10 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test - fun detectDoesNotRunWhenNonStrongBiometricIsAllowed() = + fun detectDoesNotRunWhenFaceAuthIsCurrentlyAllowedToRun() = testScope.runTest { testGatingCheckForDetect { - biometricSettingsRepository.setIsNonStrongBiometricAllowed(true) - } - } - - @Test - fun detectDoesNotRunWhenStrongBiometricIsAllowedAndFaceAuthSensorStrengthIsStrong() = - testScope.runTest { - fakeFacePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG)) - runCurrent() - - testGatingCheckForDetect(isFaceStrong = true) { - biometricSettingsRepository.setIsStrongBiometricAllowed(true) - // this shouldn't matter as face is set as a strong sensor - biometricSettingsRepository.setIsNonStrongBiometricAllowed(false) + biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true) } } @@ -1043,12 +1008,9 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { faceAuthenticateIsCalled() } - private suspend fun TestScope.testGatingCheckForFaceAuth( - isFaceStrong: Boolean = false, - gatingCheckModifier: () -> Unit - ) { + private suspend fun TestScope.testGatingCheckForFaceAuth(gatingCheckModifier: () -> Unit) { initCollectors() - allPreconditionsToRunFaceAuthAreTrue(isFaceStrong) + allPreconditionsToRunFaceAuthAreTrue() gatingCheckModifier() runCurrent() @@ -1057,7 +1019,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { assertThat(underTest.canRunFaceAuth.value).isFalse() // flip the gating check back on. - allPreconditionsToRunFaceAuthAreTrue(isFaceStrong) + allPreconditionsToRunFaceAuthAreTrue() triggerFaceAuth(false) @@ -1076,19 +1038,12 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { faceAuthenticateIsNotCalled() } - private suspend fun TestScope.testGatingCheckForDetect( - isFaceStrong: Boolean = false, - gatingCheckModifier: () -> Unit - ) { + private suspend fun TestScope.testGatingCheckForDetect(gatingCheckModifier: () -> Unit) { initCollectors() allPreconditionsToRunFaceAuthAreTrue() - if (isFaceStrong) { - biometricSettingsRepository.setStrongBiometricAllowed(false) - } else { - // This will stop face auth from running but is required to be false for detect. - biometricSettingsRepository.setIsNonStrongBiometricAllowed(false) - } + // This will stop face auth from running but is required to be false for detect. + biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false) runCurrent() assertThat(canFaceAuthRun()).isFalse() @@ -1123,13 +1078,9 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true } } - private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue( - isFaceStrong: Boolean = false - ) { + private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue() { verify(faceManager, atLeastOnce()) .addLockoutResetCallback(faceLockoutResetCallback.capture()) - biometricSettingsRepository.setFaceEnrolled(true) - biometricSettingsRepository.setIsFaceAuthEnabled(true) underTest.resumeFaceAuth() trustRepository.setCurrentUserTrusted(false) keyguardRepository.setKeyguardGoingAway(false) @@ -1140,14 +1091,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { WakeSleepReason.OTHER ) ) - if (isFaceStrong) { - biometricSettingsRepository.setStrongBiometricAllowed(true) - } else { - biometricSettingsRepository.setIsNonStrongBiometricAllowed(true) - } + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) + biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true) + biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true) biometricSettingsRepository.setIsUserInLockdown(false) fakeUserRepository.setSelectedUserInfo(primaryUser) - biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true) faceLockoutResetCallback.value.onLockoutReset(0) bouncerRepository.setAlternateVisible(true) keyguardRepository.setKeyguardShowing(true) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt index 8c98aea6a990..e91e9559fa1e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt @@ -22,32 +22,24 @@ import com.android.systemui.keyguard.shared.model.AuthenticationFlags import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map class FakeBiometricSettingsRepository : BiometricSettingsRepository { + private val _isFingerprintEnrolledAndEnabled = MutableStateFlow(false) + override val isFingerprintEnrolledAndEnabled: StateFlow<Boolean> + get() = _isFingerprintEnrolledAndEnabled - private val _isFingerprintEnrolled = MutableStateFlow<Boolean>(false) - override val isFingerprintEnrolled: StateFlow<Boolean> = _isFingerprintEnrolled.asStateFlow() + private val _isFingerprintAuthCurrentlyAllowed = MutableStateFlow(false) + override val isFingerprintAuthCurrentlyAllowed: StateFlow<Boolean> + get() = _isFingerprintAuthCurrentlyAllowed - private val _isFaceEnrolled = MutableStateFlow(false) - override val isFaceEnrolled: Flow<Boolean> - get() = _isFaceEnrolled + private val _isFaceAuthEnrolledAndEnabled = MutableStateFlow(false) + override val isFaceAuthEnrolledAndEnabled: Flow<Boolean> + get() = _isFaceAuthEnrolledAndEnabled - private val _isFaceAuthEnabled = MutableStateFlow(false) - override val isFaceAuthenticationEnabled: Flow<Boolean> - get() = _isFaceAuthEnabled - - private val _isStrongBiometricAllowed = MutableStateFlow(false) - override val isStrongBiometricAllowed = _isStrongBiometricAllowed.asStateFlow() - - private val _isNonStrongBiometricAllowed = MutableStateFlow(false) - override val isNonStrongBiometricAllowed: StateFlow<Boolean> - get() = _isNonStrongBiometricAllowed - - private val _isFingerprintEnabledByDevicePolicy = MutableStateFlow(false) - override val isFingerprintEnabledByDevicePolicy = - _isFingerprintEnabledByDevicePolicy.asStateFlow() + private val _isFaceAuthCurrentlyAllowed = MutableStateFlow(false) + override val isFaceAuthCurrentlyAllowed: Flow<Boolean> + get() = _isFaceAuthCurrentlyAllowed private val _isFaceAuthSupportedInCurrentPosture = MutableStateFlow(false) override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean> @@ -59,34 +51,33 @@ class FakeBiometricSettingsRepository : BiometricSettingsRepository { private val _authFlags = MutableStateFlow(AuthenticationFlags(0, 0)) override val authenticationFlags: Flow<AuthenticationFlags> get() = _authFlags - fun setFingerprintEnrolled(isFingerprintEnrolled: Boolean) { - _isFingerprintEnrolled.value = isFingerprintEnrolled + + fun setAuthenticationFlags(value: AuthenticationFlags) { + _authFlags.value = value } - fun setStrongBiometricAllowed(isStrongBiometricAllowed: Boolean) { - _isStrongBiometricAllowed.value = isStrongBiometricAllowed + fun setIsFingerprintAuthEnrolledAndEnabled(value: Boolean) { + _isFingerprintEnrolledAndEnabled.value = value + _isFingerprintAuthCurrentlyAllowed.value = _isFingerprintAuthCurrentlyAllowed.value && value } - fun setFingerprintEnabledByDevicePolicy(isFingerprintEnabledByDevicePolicy: Boolean) { - _isFingerprintEnabledByDevicePolicy.value = isFingerprintEnabledByDevicePolicy + fun setIsFingerprintAuthCurrentlyAllowed(value: Boolean) { + _isFingerprintAuthCurrentlyAllowed.value = value } - fun setAuthenticationFlags(value: AuthenticationFlags) { - _authFlags.value = value + fun setIsFaceAuthEnrolledAndEnabled(value: Boolean) { + _isFaceAuthEnrolledAndEnabled.value = value + _isFaceAuthCurrentlyAllowed.value = _isFaceAuthCurrentlyAllowed.value && value } - fun setFaceEnrolled(isFaceEnrolled: Boolean) { - _isFaceEnrolled.value = isFaceEnrolled + fun setIsFaceAuthCurrentlyAllowed(value: Boolean) { + _isFaceAuthCurrentlyAllowed.value = value } fun setIsFaceAuthSupportedInCurrentPosture(value: Boolean) { _isFaceAuthSupportedInCurrentPosture.value = value } - fun setIsFaceAuthEnabled(enabled: Boolean) { - _isFaceAuthEnabled.value = enabled - } - fun setIsUserInLockdown(value: Boolean) { if (value) { setAuthenticationFlags( @@ -105,12 +96,4 @@ class FakeBiometricSettingsRepository : BiometricSettingsRepository { ) } } - - fun setIsNonStrongBiometricAllowed(value: Boolean) { - _isNonStrongBiometricAllowed.value = value - } - - fun setIsStrongBiometricAllowed(value: Boolean) { - _isStrongBiometricAllowed.value = value - } } |