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

Commit 635a48b3 authored by Chandru S's avatar Chandru S Committed by Automerger Merge Worker
Browse files

Whenever face auth is locked out, provide the locked out error message in...

Whenever face auth is locked out, provide the locked out error message in response to face auth triggers am: f2c66dee

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/23806503



Change-Id: I0a198a096c6d865dcbe3958869e86f059240ca59
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 6e2f72f5 f2c66dee
Loading
Loading
Loading
Loading
+23 −4
Original line number Diff line number Diff line
@@ -16,9 +16,12 @@

package com.android.systemui.keyguard.domain.interactor

import android.content.Context
import android.hardware.biometrics.BiometricFaceConstants
import com.android.keyguard.FaceAuthUiEvent
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.CoreStartable
import com.android.systemui.R
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
@@ -27,6 +30,8 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.shared.model.AuthenticationStatus
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.util.kotlin.pairwise
@@ -34,7 +39,9 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
@@ -50,6 +57,7 @@ import kotlinx.coroutines.launch
class SystemUIKeyguardFaceAuthInteractor
@Inject
constructor(
    private val context: Context,
    @Application private val applicationScope: CoroutineScope,
    @Main private val mainDispatcher: CoroutineDispatcher,
    private val repository: DeviceEntryFaceAuthRepository,
@@ -157,18 +165,29 @@ constructor(
        repository.cancel()
    }

    private val _authenticationStatusOverride = MutableStateFlow<AuthenticationStatus?>(null)
    /** Provide the status of face authentication */
    override val authenticationStatus = repository.authenticationStatus
    override val authenticationStatus =
        merge(_authenticationStatusOverride.filterNotNull(), repository.authenticationStatus)

    /** Provide the status of face detection */
    override val detectionStatus = repository.detectionStatus

    private fun runFaceAuth(uiEvent: FaceAuthUiEvent, fallbackToDetect: Boolean) {
        if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
            if (repository.isLockedOut.value) {
                _authenticationStatusOverride.value =
                    ErrorAuthenticationStatus(
                        BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT,
                        context.resources.getString(R.string.keyguard_face_unlock_unavailable)
                    )
            } else {
                _authenticationStatusOverride.value = null
                applicationScope.launch {
                    faceAuthenticationLogger.authRequested(uiEvent)
                    repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect)
                }
            }
        } else {
            faceAuthenticationLogger.ignoredFaceAuthTrigger(
                uiEvent,
+7 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.shared.model

import android.hardware.face.FaceManager
import android.os.SystemClock.elapsedRealtime

/**
 * Authentication status provided by
@@ -38,8 +39,12 @@ data class AcquiredAuthenticationStatus(val acquiredInfo: Int) : AuthenticationS
object FailedAuthenticationStatus : AuthenticationStatus()

/** Face authentication error message */
data class ErrorAuthenticationStatus(val msgId: Int, val msg: String? = null) :
    AuthenticationStatus() {
data class ErrorAuthenticationStatus(
    val msgId: Int,
    val msg: String? = null,
    // present to break equality check if the same error occurs repeatedly.
    val createdAt: Long = elapsedRealtime()
) : AuthenticationStatus() {
    /**
     * Method that checks if [msgId] is a lockout error. A lockout error means that face
     * authentication is locked out.
+3 −7
Original line number Diff line number Diff line
@@ -418,13 +418,9 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
                FACE_ERROR_CANCELED,
                "First auth attempt cancellation completed"
            )
            assertThat(authStatus())
                .isEqualTo(
                    ErrorAuthenticationStatus(
                        FACE_ERROR_CANCELED,
                        "First auth attempt cancellation completed"
                    )
                )
            val value = authStatus() as ErrorAuthenticationStatus
            assertThat(value.msgId).isEqualTo(FACE_ERROR_CANCELED)
            assertThat(value.msg).isEqualTo("First auth attempt cancellation completed")

            faceAuthenticateIsCalled()
            uiEventIsLogged(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+20 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@

package com.android.systemui.keyguard.domain.interactor

import android.hardware.biometrics.BiometricFaceConstants
import android.os.Handler
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -30,6 +31,7 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInte
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -39,6 +41,7 @@ import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepo
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -90,6 +93,7 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {

        underTest =
            SystemUIKeyguardFaceAuthInteractor(
                mContext,
                testScope.backgroundScope,
                dispatcher,
                faceAuthRepository,
@@ -143,6 +147,22 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
                )
        }

    @Test
    fun whenFaceIsLockedOutAnyAttemptsToTriggerFaceAuthMustProvideLockoutError() =
        testScope.runTest {
            underTest.start()
            val authenticationStatus = collectLastValue(underTest.authenticationStatus)
            faceAuthRepository.setLockedOut(true)

            underTest.onDeviceLifted()

            val outputValue = authenticationStatus()!! as ErrorAuthenticationStatus
            assertThat(outputValue.msgId)
                .isEqualTo(BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT)
            assertThat(outputValue.msg).isEqualTo("Face Unlock unavailable")
            assertThat(faceAuthRepository.runningAuthRequest.value).isNull()
        }

    @Test
    fun faceAuthIsRequestedWhenLockscreenBecomesVisibleFromAodState() =
        testScope.runTest {
+7 −1
Original line number Diff line number Diff line
@@ -41,7 +41,9 @@ class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository {
    fun setDetectionStatus(status: DetectionStatus) {
        _detectionStatus.value = status
    }
    override val isLockedOut = MutableStateFlow(false)

    private val _isLockedOut = MutableStateFlow(false)
    override val isLockedOut = _isLockedOut
    private val _runningAuthRequest = MutableStateFlow<Pair<FaceAuthUiEvent, Boolean>?>(null)
    val runningAuthRequest: StateFlow<Pair<FaceAuthUiEvent, Boolean>?> =
        _runningAuthRequest.asStateFlow()
@@ -56,6 +58,10 @@ class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository {
        _isAuthRunning.value = true
    }

    fun setLockedOut(value: Boolean) {
        _isLockedOut.value = value
    }

    override fun cancel() {
        _isAuthRunning.value = false
        _runningAuthRequest.value = null