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

Commit d53ce56d authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Don't let face failures override face lockout messages" into main

parents f34f79ae d5fa6ab5
Loading
Loading
Loading
Loading
+32 −5
Original line number Diff line number Diff line
@@ -39,7 +39,6 @@ import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepositor
import com.android.systemui.bouncer.shared.model.BouncerMessageModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFingerprintAuthInteractor
import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
@@ -114,8 +113,6 @@ class BouncerMessageInteractorTest : SysuiTestCase() {
                systemPropertiesHelper = systemPropertiesHelper,
                primaryBouncerInteractor = kosmos.primaryBouncerInteractor,
                facePropertyRepository = kosmos.fakeFacePropertyRepository,
                deviceEntryFingerprintAuthInteractor = kosmos.deviceEntryFingerprintAuthInteractor,
                faceAuthRepository = kosmos.fakeDeviceEntryFaceAuthRepository,
                securityModel = securityModel,
                deviceEntryBiometricsAllowedInteractor =
                    kosmos.deviceEntryBiometricsAllowedInteractor,
@@ -217,7 +214,7 @@ class BouncerMessageInteractorTest : SysuiTestCase() {
    fun resetMessageBackToDefault_faceAuthRestarts() =
        testScope.runTest {
            init()
            verify(updateMonitor).registerCallback(keyguardUpdateMonitorCaptor.capture())
            captureKeyguardUpdateMonitorCallback()
            val bouncerMessage by collectLastValue(underTest.bouncerMessage)

            underTest.setFaceAcquisitionMessage("not empty")
@@ -240,7 +237,7 @@ class BouncerMessageInteractorTest : SysuiTestCase() {
    fun faceRestartDoesNotResetFingerprintMessage() =
        testScope.runTest {
            init()
            verify(updateMonitor).registerCallback(keyguardUpdateMonitorCaptor.capture())
            captureKeyguardUpdateMonitorCallback()
            val bouncerMessage by collectLastValue(underTest.bouncerMessage)

            underTest.setFingerprintAcquisitionMessage("not empty")
@@ -336,6 +333,32 @@ class BouncerMessageInteractorTest : SysuiTestCase() {
            assertThat(lockoutMessage?.secondaryMessage?.message).isNull()
        }

    @Test
    fun faceLockoutThenFaceFailure_doesNotUpdateMessage() =
        testScope.runTest {
            init()
            captureKeyguardUpdateMonitorCallback()
            val bouncerMessage by collectLastValue(underTest.bouncerMessage)

            kosmos.fakeBiometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
            kosmos.fakeDeviceEntryFaceAuthRepository.setLockedOut(true)
            runCurrent()

            assertThat(primaryResMessage(bouncerMessage))
                .isEqualTo("Unlock with PIN or fingerprint")
            assertThat(secondaryResMessage(bouncerMessage))
                .isEqualTo("Can’t unlock with face. Too many attempts.")

            // WHEN face failure comes in during lockout
            keyguardUpdateMonitorCaptor.value.onBiometricAuthFailed(BiometricSourceType.FACE)

            // THEN lockout message does NOT update to face failure message
            assertThat(primaryResMessage(bouncerMessage))
                .isEqualTo("Unlock with PIN or fingerprint")
            assertThat(secondaryResMessage(bouncerMessage))
                .isEqualTo("Can’t unlock with face. Too many attempts.")
        }

    @Test
    fun onFaceLockoutStateChange_whenFaceIsNotEnrolled_isANoop() =
        testScope.runTest {
@@ -629,6 +652,10 @@ class BouncerMessageInteractorTest : SysuiTestCase() {
        }
    }

    private fun captureKeyguardUpdateMonitorCallback() {
        verify(updateMonitor).registerCallback(keyguardUpdateMonitorCaptor.capture())
    }

    companion object {
        private const val PRIMARY_USER_ID = 0
        private val PRIMARY_USER =
+14 −0
Original line number Diff line number Diff line
@@ -369,4 +369,18 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
            invokeOnCallback { it.onStrongAuthStateChanged(0) }
            assertThat(shouldUpdateIndicatorVisibility).isTrue()
        }

    @Test
    fun isLockedOut_initialStateFalse() =
        testScope.runTest {
            whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
            assertThat(underTest.isLockedOut.value).isEqualTo(false)
        }

    @Test
    fun isLockedOut_initialStateTrue() =
        testScope.runTest {
            whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)
            assertThat(underTest.isLockedOut.value).isEqualTo(true)
        }
}
+13 −6
Original line number Diff line number Diff line
@@ -33,9 +33,7 @@ import com.android.systemui.bouncer.shared.model.BouncerMessageStrings
import com.android.systemui.bouncer.shared.model.Message
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
@@ -75,8 +73,6 @@ constructor(
    primaryBouncerInteractor: PrimaryBouncerInteractor,
    @Application private val applicationScope: CoroutineScope,
    private val facePropertyRepository: FacePropertyRepository,
    private val deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
    faceAuthRepository: DeviceEntryFaceAuthRepository,
    private val securityModel: KeyguardSecurityModel,
    deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
) {
@@ -97,6 +93,17 @@ constructor(
    private val kumCallback =
        object : KeyguardUpdateMonitorCallback() {
            override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) {
                // Only show the biometric failure messages if the biometric is NOT locked out.
                // If the biometric is locked out, rely on the lock out message to show
                // the lockout message & don't override it with the failure message.
                if (
                    (biometricSourceType == BiometricSourceType.FACE &&
                        deviceEntryBiometricsAllowedInteractor.isFaceLockedOut.value) ||
                        (biometricSourceType == BiometricSourceType.FINGERPRINT &&
                            deviceEntryBiometricsAllowedInteractor.isFingerprintLockedOut.value)
                ) {
                    return
                }
                repository.setMessage(
                    when (biometricSourceType) {
                        BiometricSourceType.FINGERPRINT ->
@@ -159,8 +166,8 @@ constructor(
                biometricSettingsRepository.authenticationFlags,
                trustRepository.isCurrentUserTrustManaged,
                isAnyBiometricsEnabledAndEnrolled,
                deviceEntryFingerprintAuthInteractor.isLockedOut,
                faceAuthRepository.isLockedOut,
                deviceEntryBiometricsAllowedInteractor.isFingerprintLockedOut,
                deviceEntryBiometricsAllowedInteractor.isFaceLockedOut,
                isFingerprintAuthCurrentlyAllowedOnBouncer,
                ::Septuple
            )
+17 −4
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
@@ -44,17 +45,29 @@ constructor(
    biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
    facePropertyRepository: FacePropertyRepository,
) {
    /**
     * Whether face is locked out due to too many failed face attempts. This currently includes
     * whether face is not allowed based on other biometric lockouts; however does not include if
     * face isn't allowed due to other strong or primary authentication requirements.
     */
    val isFaceLockedOut: StateFlow<Boolean> = deviceEntryFaceAuthInteractor.isLockedOut

    private val isStrongFaceAuth: Flow<Boolean> =
        facePropertyRepository.sensorInfo.map { it?.strength == SensorStrength.STRONG }

    private val isStrongFaceAuthLockedOut: Flow<Boolean> =
        combine(isStrongFaceAuth, deviceEntryFaceAuthInteractor.isLockedOut) {
            isStrongFaceAuth,
            isFaceAuthLockedOut ->
        combine(isStrongFaceAuth, isFaceLockedOut) { isStrongFaceAuth, isFaceAuthLockedOut ->
            isStrongFaceAuth && isFaceAuthLockedOut
        }

    /**
     * Whether fingerprint is locked out due to too many failed fingerprint attempts. This does NOT
     * include whether fingerprint is not allowed based on other biometric lockouts nor if
     * fingerprint isn't allowed due to other strong or primary authentication requirements.
     */
    val isFingerprintLockedOut: StateFlow<Boolean> =
        deviceEntryFingerprintAuthInteractor.isLockedOut

    /**
     * Whether fingerprint authentication is currently allowed for the user. This is true if the
     * user has fingerprint auth enabled, enrolled, it is not disabled by any security timeouts by
@@ -64,7 +77,7 @@ constructor(
     */
    val isFingerprintAuthCurrentlyAllowed: Flow<Boolean> =
        combine(
            deviceEntryFingerprintAuthInteractor.isLockedOut,
            isFingerprintLockedOut,
            biometricSettingsInteractor.fingerprintAuthCurrentlyAllowed,
            isStrongFaceAuthLockedOut,
        ) { fpLockedOut, fpAllowedBySettings, strongAuthFaceAuthLockedOut ->
+1 −1
Original line number Diff line number Diff line
@@ -54,7 +54,7 @@ constructor(
    val authenticationStatus: Flow<FingerprintAuthenticationStatus> =
        repository.authenticationStatus

    val isLockedOut: Flow<Boolean> = repository.isLockedOut
    val isLockedOut: StateFlow<Boolean> = repository.isLockedOut

    val fingerprintFailure: Flow<FailFingerprintAuthenticationStatus> =
        repository.authenticationStatus.filterIsInstance<FailFingerprintAuthenticationStatus>()
Loading