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

Commit 0889fb51 authored by Chandru S's avatar Chandru S Committed by Android (Google) Code Review
Browse files

Merge "Retry face auth whenever there is a hardware error" into udc-dev

parents e47c0ec0 0d775341
Loading
Loading
Loading
Loading
+44 −8
Original line number Diff line number Diff line
@@ -140,8 +140,10 @@ constructor(
    private var authCancellationSignal: CancellationSignal? = null
    private var detectCancellationSignal: CancellationSignal? = null
    private var faceAcquiredInfoIgnoreList: Set<Int>
    private var retryCount = 0

    private var cancelNotReceivedHandlerJob: Job? = null
    private var halErrorRetryJob: Job? = null

    private val _authenticationStatus: MutableStateFlow<AuthenticationStatus?> =
        MutableStateFlow(null)
@@ -228,6 +230,8 @@ constructor(
            .onEach { goingAwayOrUserSwitchingInProgress ->
                if (goingAwayOrUserSwitchingInProgress) {
                    _isAuthenticated.value = false
                    retryCount = 0
                    halErrorRetryJob?.cancel()
                }
            }
            .launchIn(applicationScope)
@@ -385,14 +389,11 @@ constructor(
                _authenticationStatus.value = errorStatus
                _isAuthenticated.value = false
                if (errorStatus.isCancellationError()) {
                    cancelNotReceivedHandlerJob?.cancel()
                    applicationScope.launch {
                        faceAuthLogger.launchingQueuedFaceAuthRequest(
                            faceAuthRequestedWhileCancellation
                        )
                        faceAuthRequestedWhileCancellation?.let { authenticate(it) }
                        faceAuthRequestedWhileCancellation = null
                    handleFaceCancellationError()
                }
                if (errorStatus.isHardwareError()) {
                    faceAuthLogger.hardwareError(errorStatus)
                    handleFaceHardwareError()
                }
                faceAuthLogger.authenticationError(
                    errorCode,
@@ -418,6 +419,35 @@ constructor(
            }
        }

    private fun handleFaceCancellationError() {
        cancelNotReceivedHandlerJob?.cancel()
        applicationScope.launch {
            faceAuthRequestedWhileCancellation?.let {
                faceAuthLogger.launchingQueuedFaceAuthRequest(it)
                authenticate(it)
            }
            faceAuthRequestedWhileCancellation = null
        }
    }

    private fun handleFaceHardwareError() {
        if (retryCount < HAL_ERROR_RETRY_MAX) {
            retryCount++
            halErrorRetryJob?.cancel()
            halErrorRetryJob =
                applicationScope.launch {
                    delay(HAL_ERROR_RETRY_TIMEOUT)
                    if (retryCount < HAL_ERROR_RETRY_MAX) {
                        faceAuthLogger.attemptingRetryAfterHardwareError(retryCount)
                        authenticate(
                            FaceAuthUiEvent.FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE,
                            fallbackToDetection = false
                        )
                    }
                }
        }
    }

    private fun onFaceAuthRequestCompleted() {
        cancellationInProgress = false
        _isAuthRunning.value = false
@@ -558,6 +588,12 @@ constructor(
         * cancelled.
         */
        const val DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000L

        /** Number of allowed retries whenever there is a face hardware error */
        const val HAL_ERROR_RETRY_MAX = 20

        /** Timeout before retries whenever there is a HAL error. */
        const val HAL_ERROR_RETRY_TIMEOUT = 500L // ms
    }

    override fun dump(pw: PrintWriter, args: Array<out String>) {
+5 −0
Original line number Diff line number Diff line
@@ -50,6 +50,11 @@ data class ErrorAuthenticationStatus(val msgId: Int, val msg: String?) : Authent
     * was cancelled before it completed.
     */
    fun isCancellationError() = msgId == FaceManager.FACE_ERROR_CANCELED

    /** Method that checks if [msgId] is a hardware error. */
    fun isHardwareError() =
        msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE ||
            msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS
}

/** Face detection success message. */
+22 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ import android.hardware.face.FaceManager
import android.hardware.face.FaceSensorPropertiesInternal
import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.log.dagger.FaceAuthLog
import com.android.systemui.plugins.log.LogBuffer
@@ -239,4 +240,25 @@ constructor(
            { "Requesting face auth for trigger: $str1" }
        )
    }

    fun hardwareError(errorStatus: ErrorAuthenticationStatus) {
        logBuffer.log(
            TAG,
            DEBUG,
            {
                str1 = "${errorStatus.msg}"
                int1 = errorStatus.msgId
            },
            { "Received face hardware error: $str1 , code: $int1" }
        )
    }

    fun attemptingRetryAfterHardwareError(retryCount: Int) {
        logBuffer.log(
            TAG,
            DEBUG,
            { int1 = retryCount },
            { "Attempting face auth again because of HW error: retry attempt $int1" }
        )
    }
}
+21 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.app.StatusBarManager.SESSION_KEYGUARD
import android.content.pm.UserInfo
import android.content.pm.UserInfo.FLAG_PRIMARY
import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED
import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE
import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT
import android.hardware.biometrics.ComponentInfoInternal
import android.hardware.face.FaceAuthenticateOptions
@@ -824,6 +825,26 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
            verify(faceManager).scheduleWatchdog()
        }

    @Test
    fun retryFaceIfThereIsAHardwareError() =
        testScope.runTest {
            initCollectors()
            allPreconditionsToRunFaceAuthAreTrue()

            triggerFaceAuth(fallbackToDetect = false)
            clearInvocations(faceManager)

            authenticationCallback.value.onAuthenticationError(
                FACE_ERROR_HW_UNAVAILABLE,
                "HW unavailable"
            )

            advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.HAL_ERROR_RETRY_TIMEOUT)
            runCurrent()

            faceAuthenticateIsCalled()
        }

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