summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Beverly <beverlyt@google.com> 2024-01-31 20:13:43 +0000
committer Beverly <beverlyt@google.com> 2024-02-15 20:16:46 +0000
commit560fa5f255102e87a8fad64bb9723aa1624efba3 (patch)
tree495f88b947035193fe860959c9211ae86becc745
parent99dace73287ccac065f1472056e98175c16693a5 (diff)
Add face help message deferral logic to BiometricMessageInteractor
Update logging tags so the instances of FaceHelpMessageDeferrals are distinct. Test: atest BiometricMessageInteractorTest Fixes: 317215391 Flag: ACONFIG com.android.systemui.device_entry_udfps_refactor DEVELOPMENT Change-Id: I664a071becdc49e7b70bcaea0a108b418edcdd64
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt112
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt56
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/FaceHelpMessageDeferralKosmos.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractorKosmos.kt33
12 files changed, 284 insertions, 48 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
index 059620502a49..b31f6f5b096b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
@@ -220,6 +220,7 @@ class FaceHelpMessageDeferralTest : SysuiTestCase() {
threshold,
logger,
dumpManager,
+ "0",
)
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
index 2d854d934ca2..02ee2c951453 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
@@ -16,18 +16,8 @@
package com.android.keyguard.logging
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel.DEBUG
-import com.android.systemui.log.dagger.BiometricLog
-import javax.inject.Inject
-
-/** Helper class for logging for [com.android.systemui.biometrics.FaceHelpMessageDeferral] */
-@SysUISingleton
-class FaceMessageDeferralLogger
-@Inject
-constructor(@BiometricLog private val logBuffer: LogBuffer) :
- BiometricMessageDeferralLogger(logBuffer, "FaceMessageDeferralLogger")
open class BiometricMessageDeferralLogger(
private val logBuffer: LogBuffer,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
index 8c68eac84963..90d06fb0bec1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
@@ -18,14 +18,16 @@ package com.android.systemui.biometrics
import android.content.res.Resources
import com.android.keyguard.logging.BiometricMessageDeferralLogger
-import com.android.keyguard.logging.FaceMessageDeferralLogger
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.dagger.BiometricLog
import com.android.systemui.res.R
import java.io.PrintWriter
import java.util.Objects
+import java.util.UUID
import javax.inject.Inject
@SysUISingleton
@@ -33,14 +35,16 @@ class FaceHelpMessageDeferralFactory
@Inject
constructor(
@Main private val resources: Resources,
- private val logBuffer: FaceMessageDeferralLogger,
+ @BiometricLog private val logBuffer: LogBuffer,
private val dumpManager: DumpManager
) {
fun create(): FaceHelpMessageDeferral {
+ val id = UUID.randomUUID().toString()
return FaceHelpMessageDeferral(
resources = resources,
- logBuffer = logBuffer,
+ logBuffer = BiometricMessageDeferralLogger(logBuffer, "FaceHelpMessageDeferral[$id]"),
dumpManager = dumpManager,
+ id = id,
)
}
}
@@ -51,15 +55,17 @@ constructor(
*/
class FaceHelpMessageDeferral(
resources: Resources,
- logBuffer: FaceMessageDeferralLogger,
- dumpManager: DumpManager
+ logBuffer: BiometricMessageDeferralLogger,
+ dumpManager: DumpManager,
+ val id: String,
) :
BiometricMessageDeferral(
resources.getIntArray(R.array.config_face_help_msgs_defer_until_timeout).toHashSet(),
resources.getIntArray(R.array.config_face_help_msgs_ignore).toHashSet(),
resources.getFloat(R.dimen.config_face_help_msgs_defer_until_timeout_threshold),
logBuffer,
- dumpManager
+ dumpManager,
+ id,
)
/**
@@ -72,7 +78,8 @@ open class BiometricMessageDeferral(
private val acquiredInfoToIgnore: Set<Int>,
private val threshold: Float,
private val logBuffer: BiometricMessageDeferralLogger,
- dumpManager: DumpManager
+ dumpManager: DumpManager,
+ id: String,
) : Dumpable {
private val acquiredInfoToFrequency: MutableMap<Int, Int> = HashMap()
private val acquiredInfoToHelpString: MutableMap<Int, String> = HashMap()
@@ -80,7 +87,10 @@ open class BiometricMessageDeferral(
private var totalFrames = 0
init {
- dumpManager.registerDumpable(this.javaClass.name, this)
+ dumpManager.registerNormalDumpable(
+ "${this.javaClass.name}[$id]",
+ this,
+ )
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
index 55d2bfcc8911..6bfe8d91b5fc 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
@@ -29,6 +29,7 @@ import com.android.systemui.deviceentry.shared.model.FingerprintMessage
import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -41,9 +42,8 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
/**
- * BiometricMessage business logic. Filters biometric error/acquired/fail/success events for
- * authentication events that should never surface a message to the user at the current device
- * state.
+ * BiometricMessage business logic. Filters biometric error/fail/success events for authentication
+ * events that should never surface a message to the user at the current device state.
*/
@ExperimentalCoroutinesApi
@SysUISingleton
@@ -54,7 +54,8 @@ constructor(
fingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
fingerprintPropertyInteractor: FingerprintPropertyInteractor,
faceAuthInteractor: DeviceEntryFaceAuthInteractor,
- biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
+ private val biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
+ faceHelpMessageDeferralInteractor: FaceHelpMessageDeferralInteractor,
) {
private val faceHelp: Flow<HelpFaceAuthenticationStatus> =
faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>()
@@ -130,19 +131,24 @@ constructor(
)
private val faceHelpMessage: Flow<FaceMessage> =
- biometricSettingsInteractor.fingerprintAndFaceEnrolledAndEnabled
- .flatMapLatest { fingerprintAndFaceEnrolledAndEnabled ->
+ faceHelp
+ .filterNot {
+ // Message deferred to potentially show at face timeout error instead
+ faceHelpMessageDeferralInteractor.shouldDefer(it.msgId)
+ }
+ .sample(biometricSettingsInteractor.fingerprintAndFaceEnrolledAndEnabled, ::Pair)
+ .filter { (faceAuthHelpStatus, fingerprintAndFaceEnrolledAndEnabled) ->
if (fingerprintAndFaceEnrolledAndEnabled) {
- faceHelp.filter { faceAuthHelpStatus ->
- coExFaceAcquisitionMsgIdsToShow.contains(faceAuthHelpStatus.msgId)
- }
+ // Show only some face help messages if fingerprint is also enrolled
+ coExFaceAcquisitionMsgIdsToShow.contains(faceAuthHelpStatus.msgId)
} else {
- faceHelp
+ // Show all face help messages if only face is enrolled
+ true
}
}
- .sample(biometricSettingsInteractor.faceAuthCurrentlyAllowed, ::Pair)
- .filter { (_, faceAuthCurrentlyAllowed) -> faceAuthCurrentlyAllowed }
- .map { (status, _) -> FaceMessage(status.msg) }
+ .sample(biometricSettingsInteractor.faceAuthCurrentlyAllowed, ::toTriple)
+ .filter { (_, _, faceAuthCurrentlyAllowed) -> faceAuthCurrentlyAllowed }
+ .map { (status, _, _) -> FaceMessage(status.msg) }
private val faceFailureMessage: Flow<FaceMessage> =
faceFailure
@@ -159,12 +165,18 @@ constructor(
}
.map { (status, _) ->
when {
- status.isTimeoutError() -> FaceTimeoutMessage(status.msg)
+ status.isTimeoutError() -> {
+ val deferredMessage = faceHelpMessageDeferralInteractor.getDeferredMessage()
+ if (deferredMessage != null) {
+ FaceMessage(deferredMessage.toString())
+ } else {
+ FaceTimeoutMessage(status.msg)
+ }
+ }
else -> FaceMessage(status.msg)
}
}
- // TODO(b/317215391): support showing face acquired messages on timeout + face lockout errors
val faceMessage: Flow<FaceMessage> =
merge(
faceHelpMessage,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt
index 4515fcb545b3..96171aa6566e 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt
@@ -33,6 +33,7 @@ constructor(
) {
val fingerprintAuthCurrentlyAllowed: Flow<Boolean> =
repository.isFingerprintAuthCurrentlyAllowed
+ val faceAuthEnrolledAndEnabled: Flow<Boolean> = repository.isFaceAuthEnrolledAndEnabled
val faceAuthCurrentlyAllowed: Flow<Boolean> = repository.isFaceAuthCurrentlyAllowed
/** Whether both fingerprint and face are enrolled and enabled for device entry. */
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
new file mode 100644
index 000000000000..fd6fbc9610f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import android.hardware.face.FaceManager
+import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
+import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.launch
+
+/**
+ * FaceHelpMessageDeferral business logic. Processes face acquired and face help authentication
+ * events to determine whether a face auth event should be displayed to the user immediately or when
+ * a [FaceManager.FACE_ERROR_TIMEOUT] is received.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class FaceHelpMessageDeferralInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ faceAuthInteractor: DeviceEntryFaceAuthInteractor,
+ private val biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
+ faceHelpMessageDeferralFactory: FaceHelpMessageDeferralFactory,
+) {
+ private val faceHelpMessageDeferral = faceHelpMessageDeferralFactory.create()
+ private val faceAcquired: Flow<AcquiredFaceAuthenticationStatus> =
+ faceAuthInteractor.authenticationStatus.filterIsInstance<AcquiredFaceAuthenticationStatus>()
+ private val faceHelp: Flow<HelpFaceAuthenticationStatus> =
+ faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>()
+
+ init {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ startUpdatingFaceHelpMessageDeferral()
+ }
+ }
+
+ /**
+ * If the given [HelpFaceAuthenticationStatus] msgId should be deferred to
+ * [FaceManager.FACE_ERROR_TIMEOUT].
+ */
+ fun shouldDefer(msgId: Int): Boolean {
+ return faceHelpMessageDeferral.shouldDefer(msgId)
+ }
+
+ /**
+ * Message that was deferred to show at [FaceManager.FACE_ERROR_TIMEOUT], if any. Returns null
+ * if there are currently no valid deferred messages.
+ */
+ fun getDeferredMessage(): CharSequence? {
+ return faceHelpMessageDeferral.getDeferredMessage()
+ }
+
+ private fun startUpdatingFaceHelpMessageDeferral() {
+ scope.launch {
+ biometricSettingsInteractor.faceAuthEnrolledAndEnabled
+ .flatMapLatest { faceEnrolledAndEnabled ->
+ if (faceEnrolledAndEnabled) {
+ faceAcquired
+ } else {
+ emptyFlow()
+ }
+ }
+ .collect {
+ if (it.acquiredInfo == FaceManager.FACE_ACQUIRED_START) {
+ faceHelpMessageDeferral.reset()
+ }
+ faceHelpMessageDeferral.processFrame(it.acquiredInfo)
+ }
+ }
+
+ scope.launch {
+ biometricSettingsInteractor.faceAuthEnrolledAndEnabled
+ .flatMapLatest { faceEnrolledAndEnabled ->
+ if (faceEnrolledAndEnabled) {
+ faceHelp
+ } else {
+ emptyFlow()
+ }
+ }
+ .collect { helpAuthenticationStatus ->
+ helpAuthenticationStatus.msg?.let { msg ->
+ faceHelpMessageDeferral.updateMessage(helpAuthenticationStatus.msgId, msg)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
index 118215c6ba15..59c3f7f8aded 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
@@ -27,6 +27,7 @@ sealed class BiometricMessage(
/** Face biometric message */
open class FaceMessage(faceMessage: String?) : BiometricMessage(faceMessage)
+/** Face timeout message. */
data class FaceTimeoutMessage(
private val faceTimeoutMessage: String?,
) : FaceMessage(faceTimeoutMessage)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt
index 49c64bdcdd23..9edb4d159d4a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt
@@ -40,23 +40,17 @@ constructor(
alternateBouncerInteractor: AlternateBouncerInteractor,
) {
- private val faceHelp: Flow<FaceMessage> =
- biometricMessageInteractor.faceMessage.filterNot { faceMessage ->
- faceMessage !is FaceTimeoutMessage
- }
- private val fingerprintMessages: Flow<FingerprintMessage> =
- biometricMessageInteractor.fingerprintMessage.filterNot { fingerprintMessage ->
- // On lockout, the device will show the bouncer. Let's not show the message
- // before the transition or else it'll look flickery.
- fingerprintMessage is FingerprintLockoutMessage
- }
+ private val faceMessage: Flow<FaceMessage> =
+ biometricMessageInteractor.faceMessage.filterNot { it is FaceTimeoutMessage }
+ private val fingerprintMessage: Flow<FingerprintMessage> =
+ biometricMessageInteractor.fingerprintMessage.filterNot { it is FingerprintLockoutMessage }
val message: Flow<BiometricMessage?> =
alternateBouncerInteractor.isVisible.flatMapLatest { isVisible ->
if (isVisible) {
merge(
- faceHelp,
- fingerprintMessages,
+ faceMessage,
+ fingerprintMessage,
)
} else {
flowOf(null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt
index a53f8d43f323..5581f0c0314f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt
@@ -23,6 +23,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.biometrics.domain.faceHelpMessageDeferral
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.coroutines.collectLastValue
@@ -39,11 +40,13 @@ import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationS
import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -57,6 +60,7 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
private val fingerprintAuthRepository = kosmos.deviceEntryFingerprintAuthRepository
private val faceAuthRepository = kosmos.fakeDeviceEntryFaceAuthRepository
private val biometricSettingsRepository = kosmos.biometricSettingsRepository
+ private val faceHelpMessageDeferral = kosmos.faceHelpMessageDeferral
@Test
fun fingerprintErrorMessage() =
@@ -266,11 +270,38 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
)
)
- // THEN fingerprintFailedMessage is updated
+ // THEN fingerprintHelpMessage is updated
assertThat(faceHelpMessage).isNotNull()
}
@Test
+ fun faceHelpMessageShouldDefer() =
+ testScope.runTest {
+ val faceHelpMessage by collectLastValue(underTest.faceMessage)
+
+ // GIVEN face is allowed
+ biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
+
+ // GIVEN face only enrolled
+ biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+
+ // WHEN all face help messages should be deferred
+ whenever(faceHelpMessageDeferral.shouldDefer(anyInt())).thenReturn(true)
+
+ // WHEN authentication status help
+ faceAuthRepository.setAuthenticationStatus(
+ HelpFaceAuthenticationStatus(
+ msg = "Move left",
+ msgId = FaceManager.FACE_ACQUIRED_TOO_RIGHT,
+ )
+ )
+
+ // THEN fingerprintHelpMessage is NOT updated
+ assertThat(faceHelpMessage).isNull()
+ }
+
+ @Test
fun faceHelpMessage_coEx() =
testScope.runTest {
val faceHelpMessage by collectLastValue(underTest.faceMessage)
@@ -291,7 +322,7 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
)
)
- // THEN fingerprintFailedMessage is NOT updated
+ // THEN fingerprintHelpMessage is NOT updated
assertThat(faceHelpMessage).isNull()
}
@@ -337,7 +368,7 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
testScope.runTest {
val faceErrorMessage by collectLastValue(underTest.faceMessage)
- // WHEN authentication status error is FACE_ERROR_HW_UNAVAILABLE
+ // WHEN authentication status error is FACE_ERROR_TIMEOUT
faceAuthRepository.setAuthenticationStatus(
ErrorFaceAuthenticationStatus(msgId = FaceManager.FACE_ERROR_TIMEOUT, msg = "test")
)
@@ -349,4 +380,23 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
assertThat(faceErrorMessage).isInstanceOf(FaceTimeoutMessage::class.java)
assertThat(faceErrorMessage?.message).isEqualTo("test")
}
+
+ @Test
+ fun faceTimeoutDeferredErrorMessage() =
+ testScope.runTest {
+ whenever(faceHelpMessageDeferral.getDeferredMessage()).thenReturn("deferredMessage")
+ val faceErrorMessage by collectLastValue(underTest.faceMessage)
+
+ // WHEN authentication status error is FACE_ERROR_TIMEOUT
+ faceAuthRepository.setAuthenticationStatus(
+ ErrorFaceAuthenticationStatus(msgId = FaceManager.FACE_ERROR_TIMEOUT, msg = "test")
+ )
+
+ // GIVEN face is allowed
+ biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
+
+ // THEN faceErrorMessage is updated to deferred message instead of timeout message
+ assertThat(faceErrorMessage).isNotInstanceOf(FaceTimeoutMessage::class.java)
+ assertThat(faceErrorMessage?.message).isEqualTo("deferredMessage")
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/FaceHelpMessageDeferralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/FaceHelpMessageDeferralKosmos.kt
new file mode 100644
index 000000000000..681412117779
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/FaceHelpMessageDeferralKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.domain
+
+import com.android.systemui.biometrics.FaceHelpMessageDeferral
+import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+
+val Kosmos.faceHelpMessageDeferral by Kosmos.Fixture { mock<FaceHelpMessageDeferral>() }
+val Kosmos.faceHelpMessageDeferralFactory by
+ Kosmos.Fixture {
+ val mockFactory = mock<FaceHelpMessageDeferralFactory>()
+ whenever(mockFactory.create()).thenReturn(faceHelpMessageDeferral)
+ mockFactory
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt
index 77f48db60f41..3ea46872ebcf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt
@@ -30,5 +30,6 @@ val Kosmos.biometricMessageInteractor by
fingerprintPropertyInteractor = fingerprintPropertyInteractor,
faceAuthInteractor = deviceEntryFaceAuthInteractor,
biometricSettingsInteractor = deviceEntryBiometricSettingsInteractor,
+ faceHelpMessageDeferralInteractor = faceHelpMessageDeferralInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractorKosmos.kt
new file mode 100644
index 000000000000..724e943c9f55
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractorKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.biometrics.domain.faceHelpMessageDeferralFactory
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+val Kosmos.faceHelpMessageDeferralInteractor by
+ Kosmos.Fixture {
+ FaceHelpMessageDeferralInteractor(
+ scope = applicationCoroutineScope,
+ faceAuthInteractor = deviceEntryFaceAuthInteractor,
+ biometricSettingsInteractor = deviceEntryBiometricSettingsInteractor,
+ faceHelpMessageDeferralFactory = faceHelpMessageDeferralFactory,
+ )
+ }