Merge "Retry face auth whenever there is a hardware error" into udc-dev
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 5f6098b..05ab01b 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
@@ -140,8 +140,10 @@
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 @@
.onEach { goingAwayOrUserSwitchingInProgress ->
if (goingAwayOrUserSwitchingInProgress) {
_isAuthenticated.value = false
+ retryCount = 0
+ halErrorRetryJob?.cancel()
}
}
.launchIn(applicationScope)
@@ -385,14 +389,11 @@
_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 @@
}
}
+ 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 @@
* 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>) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
index eded9c1..c8bd958 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
@@ -50,6 +50,11 @@
* 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. */
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 7f6e4a9..efd3ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -4,6 +4,7 @@
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 @@
{ "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" }
+ )
+ }
}
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 fc75d47..a76d03b 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
@@ -21,6 +21,7 @@
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 @@
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()