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

Commit 69fcc66b authored by Chandru S's avatar Chandru S
Browse files

skip processing onAuthenticationFailed if onAuthenticationError was already...

skip processing onAuthenticationFailed if onAuthenticationError was already invoked for face lockout error


Fixes: 297977998
Test: atest DeviceEntryFaceAuthRepositoryTest
Change-Id: I811537953cc7226e8f6a15d5746fc60dbd478fa9
parent 44ff5531
Loading
Loading
Loading
Loading
+18 −14
Original line number Diff line number Diff line
@@ -56,6 +56,10 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.UserRepository
import com.google.errorprone.annotations.CompileTimeConstant
import java.io.PrintWriter
import java.util.Arrays
import java.util.stream.Collectors
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -78,10 +82,6 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.PrintWriter
import java.util.Arrays
import java.util.stream.Collectors
import javax.inject.Inject

/**
 * API to run face authentication and detection for device entry / on keyguard (as opposed to the
@@ -370,8 +370,10 @@ constructor(
                and(
                        displayStateInteractor.isDefaultDisplayOff,
                        keyguardTransitionInteractor.isFinishedInStateWhere(
                            KeyguardState::deviceIsAwakeInState),
                ).isFalse(),
                            KeyguardState::deviceIsAwakeInState
                        ),
                    )
                    .isFalse(),
                // this can happen if an app is requesting for screen off, the display can
                // turn off without wakefulness.isStartingToSleepOrAsleep calls
                "displayIsNotOffWhileFullyTransitionedToAwake",
@@ -381,10 +383,7 @@ constructor(
                "isFaceAuthEnrolledAndEnabled"
            ),
            Pair(keyguardRepository.isKeyguardGoingAway.isFalse(), "keyguardNotGoingAway"),
            Pair(
                powerInteractor.isAsleep.isFalse(),
                "deviceNotAsleep"
            ),
            Pair(powerInteractor.isAsleep.isFalse(), "deviceNotAsleep"),
            Pair(
                keyguardInteractor.isSecureCameraActive
                    .isFalse()
@@ -430,11 +429,16 @@ constructor(
    private val faceAuthCallback =
        object : FaceManager.AuthenticationCallback() {
            override fun onAuthenticationFailed() {
                _authenticationStatus.value = FailedFaceAuthenticationStatus()
                _isAuthenticated.value = false
                faceAuthLogger.authenticationFailed()
                if (!_isLockedOut.value) {
                    // onAuthenticationError gets invoked before onAuthenticationFailed when the
                    // last auth attempt locks out face authentication.
                    // Skip updating the authentication status in such a scenario.
                    _authenticationStatus.value = FailedFaceAuthenticationStatus()
                    onFaceAuthRequestCompleted()
                }
            }

            override fun onAuthenticationAcquired(acquireInfo: Int) {
                _authenticationStatus.value = AcquiredFaceAuthenticationStatus(acquireInfo)
+52 −26
Original line number Diff line number Diff line
@@ -43,7 +43,6 @@ import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMET
import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED
import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
@@ -82,6 +81,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.FakeKeyguardStateController
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.user.data.model.SelectionStatus
@@ -94,6 +94,8 @@ import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.time.SystemClock
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestDispatcher
@@ -116,8 +118,6 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
import java.io.PrintWriter
import java.io.StringWriter

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -195,9 +195,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
            }

        powerRepository = FakePowerRepository()
        powerInteractor = PowerInteractorFactory.create(
        powerInteractor =
            PowerInteractorFactory.create(
                    repository = powerRepository,
        ).powerInteractor
                )
                .powerInteractor

        val withDeps =
            KeyguardInteractorFactory.create(
@@ -635,11 +637,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {

    @Test
    fun authenticateDoesNotRunWhenDeviceIsGoingToSleep() =
        testScope.runTest {
            testGatingCheckForFaceAuth {
                powerInteractor.setAsleepForTest()
            }
        }
        testScope.runTest { testGatingCheckForFaceAuth { powerInteractor.setAsleepForTest() } }

    @Test
    fun authenticateDoesNotRunWhenSecureCameraIsActive() =
@@ -736,17 +734,21 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
            allPreconditionsToRunFaceAuthAreTrue()

            Log.i("TEST", "started waking")
            keyguardTransitionRepository.sendTransitionStep(TransitionStep(
            keyguardTransitionRepository.sendTransitionStep(
                TransitionStep(
                    from = KeyguardState.LOCKSCREEN,
                    to = KeyguardState.OFF,
                    transitionState = TransitionState.FINISHED,
            ))
                )
            )
            runCurrent()
            keyguardTransitionRepository.sendTransitionStep(TransitionStep(
            keyguardTransitionRepository.sendTransitionStep(
                TransitionStep(
                    from = KeyguardState.OFF,
                    to = KeyguardState.LOCKSCREEN,
                    transitionState = TransitionState.STARTED,
            ))
                )
            )
            runCurrent()

            Log.i("TEST", "sending display off")
@@ -766,11 +768,13 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
        testScope.runTest {
            testGatingCheckForFaceAuth {
                powerInteractor.onFinishedWakingUp()
                keyguardTransitionRepository.sendTransitionStep(TransitionStep(
                keyguardTransitionRepository.sendTransitionStep(
                    TransitionStep(
                        from = KeyguardState.OFF,
                        to = KeyguardState.LOCKSCREEN,
                        transitionState = TransitionState.FINISHED,
                ))
                    )
                )
                runCurrent()

                displayRepository.emit(
@@ -919,11 +923,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {

    @Test
    fun detectDoesNotRunWhenDeviceSleepingStartingToSleep() =
        testScope.runTest {
            testGatingCheckForDetect {
                powerInteractor.setAsleepForTest()
            }
        }
        testScope.runTest { testGatingCheckForDetect { powerInteractor.setAsleepForTest() } }

    @Test
    fun detectDoesNotRunWhenSecureCameraIsActive() =
@@ -1114,6 +1114,32 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
            biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
            faceAuthenticateIsCalled()
        }
    @Test
    fun authFailedCallAfterAuthLockedOutErrorShouldBeIgnored() =
        testScope.runTest {
            initCollectors()
            allPreconditionsToRunFaceAuthAreTrue()
            runCurrent()
            assertThat(canFaceAuthRun()).isTrue()

            underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, false)
            runCurrent()

            faceAuthenticateIsCalled()
            authenticationCallback.value.onAuthenticationError(
                FACE_ERROR_LOCKOUT_PERMANENT,
                "Too many attempts, face not available"
            )

            val lockoutError = authStatus() as ErrorFaceAuthenticationStatus
            assertThat(lockedOut()).isTrue()
            assertThat(lockoutError.isLockoutError()).isTrue()

            authenticationCallback.value.onAuthenticationFailed()
            runCurrent()

            assertThat(authStatus()).isEqualTo(lockoutError)
        }

    private suspend fun TestScope.testGatingCheckForFaceAuth(
        gatingCheckModifier: suspend () -> Unit