summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt163
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt127
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt140
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt20
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt15
7 files changed, 275 insertions, 210 deletions
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 6db21b2c1e9e..233acd90aee3 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
@@ -54,13 +54,13 @@ import com.android.systemui.log.table.TableLogBuffer
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.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
@@ -112,33 +112,27 @@ interface DeviceEntryFaceAuthRepository {
fun setLockedOut(isLockedOut: Boolean)
/**
- * Cancel current face authentication and prevent it from running until [resumeFaceAuth] is
- * invoked.
- */
- fun pauseFaceAuth()
-
- /**
- * Allow face auth paused using [pauseFaceAuth] to run again. The next invocation to
- * [authenticate] will run as long as other gating conditions don't stop it from running.
- */
- fun resumeFaceAuth()
-
- /**
- * Trigger face authentication.
+ * Request face authentication or detection to be run.
*
* [uiEvent] provided should be logged whenever face authentication runs. Invocation should be
* ignored if face authentication is already running. Results should be propagated through
* [authenticationStatus]
*
* Run only face detection when [fallbackToDetection] is true and [canRunFaceAuth] is false.
+ *
+ * Method returns immediately and the face auth request is processed as soon as possible.
*/
- suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean = false)
+ fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean = false)
/** Stop currently running face authentication or detection. */
fun cancel()
}
-@OptIn(ExperimentalCoroutinesApi::class)
+private data class AuthenticationRequest(
+ val uiEvent: FaceAuthUiEvent,
+ val fallbackToDetection: Boolean
+)
+
@SysUISingleton
class DeviceEntryFaceAuthRepositoryImpl
@Inject
@@ -171,6 +165,8 @@ constructor(
private var faceAcquiredInfoIgnoreList: Set<Int>
private var retryCount = 0
+ private var pendingAuthenticateRequest = MutableStateFlow<AuthenticationRequest?>(null)
+
private var cancelNotReceivedHandlerJob: Job? = null
private var halErrorRetryJob: Job? = null
@@ -193,15 +189,6 @@ constructor(
override val isAuthRunning: StateFlow<Boolean>
get() = _isAuthRunning
- private val faceAuthPaused = MutableStateFlow(false)
- override fun pauseFaceAuth() {
- faceAuthPaused.value = true
- }
-
- override fun resumeFaceAuth() {
- faceAuthPaused.value = false
- }
-
private val keyguardSessionId: InstanceId?
get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)
@@ -213,6 +200,8 @@ constructor(
override val isAuthenticated: Flow<Boolean>
get() = _isAuthenticated
+ private var cancellationInProgress = MutableStateFlow(false)
+
override val isBypassEnabled: Flow<Boolean> =
keyguardBypassController?.let {
conflatedCallbackFlow {
@@ -302,6 +291,7 @@ constructor(
observeFaceDetectGatingChecks()
observeFaceAuthResettingConditions()
listenForSchedulingWatchdog()
+ processPendingAuthRequests()
} else {
canRunFaceAuth = MutableStateFlow(false).asStateFlow()
canRunDetection = MutableStateFlow(false).asStateFlow()
@@ -338,6 +328,7 @@ constructor(
)
.onEach { anyOfThemIsTrue ->
if (anyOfThemIsTrue) {
+ clearPendingAuthRequest("Resetting auth status")
_isAuthenticated.value = false
retryCount = 0
halErrorRetryJob?.cancel()
@@ -346,6 +337,15 @@ constructor(
.launchIn(applicationScope)
}
+ private fun clearPendingAuthRequest(@CompileTimeConstant loggingContext: String) {
+ faceAuthLogger.clearingPendingAuthRequest(
+ loggingContext,
+ pendingAuthenticateRequest.value?.uiEvent,
+ pendingAuthenticateRequest.value?.fallbackToDetection
+ )
+ pendingAuthenticateRequest.value = null
+ }
+
private fun observeFaceDetectGatingChecks() {
canRunDetection
.onEach {
@@ -378,7 +378,6 @@ constructor(
biometricSettingsRepository.isFaceAuthEnrolledAndEnabled,
"isFaceAuthEnrolledAndEnabled"
),
- Pair(faceAuthPaused.isFalse(), "faceAuthIsNotPaused"),
Pair(keyguardRepository.isKeyguardGoingAway.isFalse(), "keyguardNotGoingAway"),
Pair(
keyguardRepository.wakefulness.map { it.isStartingToSleep() }.isFalse(),
@@ -402,7 +401,13 @@ constructor(
biometricSettingsRepository.isCurrentUserInLockdown.isFalse(),
"userHasNotLockedDownDevice"
),
- Pair(keyguardRepository.isKeyguardShowing, "isKeyguardShowing")
+ Pair(keyguardRepository.isKeyguardShowing, "isKeyguardShowing"),
+ Pair(
+ userRepository.selectedUser
+ .map { it.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS }
+ .isFalse(),
+ "userSwitchingInProgress"
+ )
)
}
@@ -440,9 +445,6 @@ constructor(
}
_authenticationStatus.value = errorStatus
_isAuthenticated.value = false
- if (errorStatus.isCancellationError()) {
- handleFaceCancellationError()
- }
if (errorStatus.isHardwareError()) {
faceAuthLogger.hardwareError(errorStatus)
handleFaceHardwareError()
@@ -471,16 +473,6 @@ constructor(
}
}
- private fun handleFaceCancellationError() {
- applicationScope.launch {
- faceAuthRequestedWhileCancellation?.let {
- faceAuthLogger.launchingQueuedFaceAuthRequest(it)
- authenticate(it)
- }
- faceAuthRequestedWhileCancellation = null
- }
- }
-
private fun handleFaceHardwareError() {
if (retryCount < HAL_ERROR_RETRY_MAX) {
retryCount++
@@ -490,7 +482,7 @@ constructor(
delay(HAL_ERROR_RETRY_TIMEOUT)
if (retryCount < HAL_ERROR_RETRY_MAX) {
faceAuthLogger.attemptingRetryAfterHardwareError(retryCount)
- authenticate(
+ requestAuthenticate(
FaceAuthUiEvent.FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE,
fallbackToDetection = false
)
@@ -501,7 +493,7 @@ constructor(
private fun onFaceAuthRequestCompleted() {
cancelNotReceivedHandlerJob?.cancel()
- cancellationInProgress = false
+ cancellationInProgress.value = false
_isAuthRunning.value = false
authCancellationSignal = null
}
@@ -512,24 +504,60 @@ constructor(
_detectionStatus.value = FaceDetectionStatus(sensorId, userId, isStrong)
}
- private var cancellationInProgress = false
- private var faceAuthRequestedWhileCancellation: FaceAuthUiEvent? = null
+ override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+ if (pendingAuthenticateRequest.value != null) {
+ faceAuthLogger.ignoredFaceAuthTrigger(
+ pendingAuthenticateRequest.value?.uiEvent,
+ "Previously queued trigger skipped due to new request"
+ )
+ }
+ faceAuthLogger.queueingRequest(uiEvent, fallbackToDetection)
+ pendingAuthenticateRequest.value = AuthenticationRequest(uiEvent, fallbackToDetection)
+ }
- override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+ private fun processPendingAuthRequests() {
+ combine(
+ pendingAuthenticateRequest,
+ canRunFaceAuth,
+ canRunDetection,
+ cancellationInProgress,
+ ) { pending, canRunAuth, canRunDetect, cancelInProgress ->
+ if (
+ pending != null &&
+ !(canRunAuth || (canRunDetect && pending.fallbackToDetection)) ||
+ cancelInProgress
+ ) {
+ faceAuthLogger.notProcessingRequestYet(
+ pending?.uiEvent,
+ canRunAuth,
+ canRunDetect,
+ cancelInProgress
+ )
+ return@combine null
+ } else {
+ return@combine pending
+ }
+ }
+ .onEach {
+ it?.let {
+ faceAuthLogger.processingRequest(it.uiEvent, it.fallbackToDetection)
+ clearPendingAuthRequest("Authenticate was invoked")
+ authenticate(it.uiEvent, it.fallbackToDetection)
+ }
+ }
+ .flowOn(mainDispatcher)
+ .launchIn(applicationScope)
+ }
+
+ private suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
if (_isAuthRunning.value) {
faceAuthLogger.ignoredFaceAuthTrigger(uiEvent, "face auth is currently running")
return
}
- if (cancellationInProgress) {
- faceAuthLogger.queuingRequestWhileCancelling(
- faceAuthRequestedWhileCancellation,
- uiEvent
- )
- faceAuthRequestedWhileCancellation = uiEvent
+ if (cancellationInProgress.value) {
+ faceAuthLogger.ignoredFaceAuthTrigger(uiEvent, "cancellation in progress")
return
- } else {
- faceAuthRequestedWhileCancellation = null
}
if (canRunFaceAuth.value) {
@@ -553,12 +581,19 @@ constructor(
FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
)
}
- } else if (fallbackToDetection && canRunDetection.value) {
- faceAuthLogger.ignoredFaceAuthTrigger(
- uiEvent,
- "face auth gating check is false, falling back to detection."
- )
- detect()
+ } else if (canRunDetection.value) {
+ if (fallbackToDetection) {
+ faceAuthLogger.ignoredFaceAuthTrigger(
+ uiEvent,
+ "face auth gating check is false, falling back to detection."
+ )
+ detect()
+ } else {
+ faceAuthLogger.ignoredFaceAuthTrigger(
+ uiEvent = uiEvent,
+ "face auth gating check is false and fallback to detection is not requested"
+ )
+ }
} else {
faceAuthLogger.ignoredFaceAuthTrigger(
uiEvent,
@@ -608,13 +643,13 @@ constructor(
faceAuthLogger.cancelSignalNotReceived(
_isAuthRunning.value,
_isLockedOut.value,
- cancellationInProgress,
- faceAuthRequestedWhileCancellation
+ cancellationInProgress.value,
+ pendingAuthenticateRequest.value?.uiEvent
)
_authenticationStatus.value = ErrorFaceAuthenticationStatus.cancelNotReceivedError()
onFaceAuthRequestCompleted()
}
- cancellationInProgress = true
+ cancellationInProgress.value = true
_isAuthRunning.value = false
}
@@ -647,9 +682,7 @@ constructor(
" supportsFaceDetection: " +
"${faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection}"
)
- pw.println(
- " faceAuthRequestedWhileCancellation: ${faceAuthRequestedWhileCancellation?.reason}"
- )
+ pw.println(" _pendingAuthenticateRequest: ${pendingAuthenticateRequest.value}")
pw.println(" authCancellationSignal: $authCancellationSignal")
pw.println(" detectCancellationSignal: $detectCancellationSignal")
pw.println(" faceAcquiredInfoIgnoreList: $faceAcquiredInfoIgnoreList")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
index 46135fa89451..c8cb9e6aa0dd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
@@ -56,9 +56,6 @@ class NoopDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceA
get() = emptyFlow()
override fun setLockedOut(isLockedOut: Boolean) = Unit
- override fun pauseFaceAuth() = Unit
-
- override fun resumeFaceAuth() = Unit
/**
* Trigger face authentication.
@@ -69,7 +66,7 @@ class NoopDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceA
*
* Run only face detection when [fallbackToDetection] is true and [canRunFaceAuth] is false.
*/
- override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {}
+ override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) = Unit
/** Stop currently running face authentication or detection. */
override fun cancel() {}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
index ccc2080d8fee..f0df3a2e6a6f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -52,8 +52,6 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield
/**
@@ -144,14 +142,11 @@ constructor(
.onEach { (previous, curr) ->
val wasSwitching = previous.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
val isSwitching = curr.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
- if (!wasSwitching && isSwitching) {
- repository.pauseFaceAuth()
- } else if (wasSwitching && !isSwitching) {
+ if (wasSwitching && !isSwitching) {
val lockoutMode = facePropertyRepository.getLockoutMode(curr.userInfo.id)
repository.setLockedOut(
lockoutMode == LockoutMode.PERMANENT || lockoutMode == LockoutMode.TIMED
)
- repository.resumeFaceAuth()
yield()
runFaceAuth(
FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING,
@@ -232,12 +227,8 @@ constructor(
)
} else {
faceAuthenticationStatusOverride.value = null
- applicationScope.launch {
- withContext(mainDispatcher) {
- faceAuthenticationLogger.authRequested(uiEvent)
- repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect)
- }
- }
+ faceAuthenticationLogger.authRequested(uiEvent)
+ repository.requestAuthenticate(uiEvent, fallbackToDetection = fallbackToDetect)
}
} else {
faceAuthenticationLogger.ignoredFaceAuthTrigger(
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 66067b11a18c..8143f99c4d0a 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -29,36 +29,18 @@ class FaceAuthenticationLogger
constructor(
@FaceAuthLog private val logBuffer: LogBuffer,
) {
- fun ignoredFaceAuthTrigger(uiEvent: FaceAuthUiEvent, ignoredReason: String) {
+ fun ignoredFaceAuthTrigger(uiEvent: FaceAuthUiEvent?, ignoredReason: String) {
logBuffer.log(
TAG,
DEBUG,
{
- str1 = uiEvent.reason
+ str1 = "${uiEvent?.reason}"
str2 = ignoredReason
},
{ "Ignoring trigger because $str2, Trigger reason: $str1" }
)
}
- fun queuingRequestWhileCancelling(
- alreadyQueuedRequest: FaceAuthUiEvent?,
- newRequest: FaceAuthUiEvent
- ) {
- logBuffer.log(
- TAG,
- DEBUG,
- {
- str1 = alreadyQueuedRequest?.reason
- str2 = newRequest.reason
- },
- {
- "Face auth requested while previous request is being cancelled, " +
- "already queued request: $str1 queueing the new request: $str2"
- }
- )
- }
-
fun authenticating(uiEvent: FaceAuthUiEvent) {
logBuffer.log(TAG, DEBUG, { str1 = uiEvent.reason }, { "Running authenticate for $str1" })
}
@@ -161,15 +143,6 @@ constructor(
)
}
- fun launchingQueuedFaceAuthRequest(faceAuthRequestedWhileCancellation: FaceAuthUiEvent?) {
- logBuffer.log(
- TAG,
- DEBUG,
- { str1 = "${faceAuthRequestedWhileCancellation?.reason}" },
- { "Received cancellation error and starting queued face auth request: $str1" }
- )
- }
-
fun faceAuthSuccess(result: FaceManager.AuthenticationResult) {
logBuffer.log(
TAG,
@@ -182,31 +155,10 @@ constructor(
)
}
- fun observedConditionChanged(newValue: Boolean, context: String) {
- logBuffer.log(
- TAG,
- DEBUG,
- {
- bool1 = newValue
- str1 = context
- },
- { "Observed condition changed: $str1, new value: $bool1" }
- )
- }
-
fun canFaceAuthRunChanged(canRun: Boolean) {
logBuffer.log(TAG, DEBUG, { bool1 = canRun }, { "canFaceAuthRun value changed to $bool1" })
}
- fun canRunDetectionChanged(canRunDetection: Boolean) {
- logBuffer.log(
- TAG,
- DEBUG,
- { bool1 = canRunDetection },
- { "canRunDetection value changed to $bool1" }
- )
- }
-
fun cancellingFaceAuth() {
logBuffer.log(TAG, DEBUG, "cancelling face auth because a gating condition became false")
}
@@ -236,7 +188,7 @@ constructor(
logBuffer.log(
TAG,
DEBUG,
- { str1 = "$uiEvent" },
+ { str1 = uiEvent.reason },
{ "Requesting face auth for trigger: $str1" }
)
}
@@ -269,4 +221,77 @@ constructor(
fun faceLockedOut(@CompileTimeConstant reason: String) {
logBuffer.log(TAG, DEBUG, "Face auth has been locked out: $reason")
}
+
+ fun queueingRequest(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = "$uiEvent"
+ bool1 = fallbackToDetection
+ },
+ { "Queueing $str1 request for face auth, fallbackToDetection: $bool1" }
+ )
+ }
+
+ fun notProcessingRequestYet(
+ uiEvent: FaceAuthUiEvent?,
+ canRunAuth: Boolean,
+ canRunDetect: Boolean,
+ cancelInProgress: Boolean
+ ) {
+ uiEvent?.let {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = uiEvent.reason
+ bool1 = canRunAuth
+ bool2 = canRunDetect
+ bool3 = cancelInProgress
+ },
+ {
+ "Waiting to process request: reason: $str1, " +
+ "canRunAuth: $bool1, " +
+ "canRunDetect: $bool2, " +
+ "cancelInProgress: $bool3"
+ }
+ )
+ }
+ }
+
+ fun processingRequest(uiEvent: FaceAuthUiEvent?, fallbackToDetection: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = "${uiEvent?.reason}"
+ bool1 = fallbackToDetection
+ },
+ { "Processing face auth request: $str1, fallbackToDetect: $bool1" }
+ )
+ }
+
+ fun clearingPendingAuthRequest(
+ @CompileTimeConstant loggingContext: String,
+ uiEvent: FaceAuthUiEvent?,
+ fallbackToDetection: Boolean?
+ ) {
+ uiEvent?.let {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = uiEvent.reason
+ str2 = "$fallbackToDetection"
+ str3 = loggingContext
+ },
+ {
+ "Clearing pending auth: $str1, " +
+ "fallbackToDetection: $str2, " +
+ "reason: $str3"
+ }
+ )
+ }
+ }
}
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 a76c88579dca..6b194f243b2c 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
@@ -36,6 +36,7 @@ import com.android.internal.logging.InstanceId.fakeInstanceId
import com.android.internal.logging.UiEventLogger
import com.android.keyguard.FaceAuthUiEvent
import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+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.R
@@ -285,7 +286,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
allPreconditionsToRunFaceAuthAreTrue()
FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER.extraInfo = 10
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
uiEventIsLogged(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
@@ -318,12 +319,12 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
clearInvocations(faceManager)
clearInvocations(uiEventLogger)
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
verifyNoMoreInteractions(faceManager)
verifyNoMoreInteractions(uiEventLogger)
}
@@ -335,7 +336,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
verify(faceManager).addLockoutResetCallback(faceLockoutResetCallback.capture())
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
authenticationCallback.value.onAuthenticationError(
@@ -389,7 +390,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
var wasAuthCancelled = false
@@ -443,7 +444,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
// Enter cancelling state
@@ -451,7 +452,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
clearInvocations(faceManager)
// Auth is while cancelling.
- underTest.authenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
// Auth is not started
verifyNoMoreInteractions(faceManager)
@@ -474,14 +475,14 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
clearInvocations(faceManager)
underTest.cancel()
advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT + 1)
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
}
@@ -492,7 +493,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
allPreconditionsToRunFaceAuthAreTrue()
val emittedValues by collectValues(underTest.authenticationStatus)
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
underTest.cancel()
advanceTimeBy(100)
underTest.cancel()
@@ -519,7 +520,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
authenticationCallback.value.onAuthenticationHelp(9, "help msg")
@@ -562,8 +563,26 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
}
@Test
- fun authenticateDoesNotRunIfFaceAuthIsCurrentlyPaused() =
- testScope.runTest { testGatingCheckForFaceAuth { underTest.pauseFaceAuth() } }
+ fun authenticateDoesNotRunIfUserSwitchingIsCurrentlyInProgress() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth {
+ fakeUserRepository.setSelectedUserInfo(
+ primaryUser,
+ SelectionStatus.SELECTION_IN_PROGRESS
+ )
+ }
+ }
+
+ @Test
+ fun detectDoesNotRunIfUserSwitchingIsCurrentlyInProgress() =
+ testScope.runTest {
+ testGatingCheckForDetect {
+ fakeUserRepository.setSelectedUserInfo(
+ userInfo = primaryUser,
+ selectionStatus = SelectionStatus.SELECTION_IN_PROGRESS
+ )
+ }
+ }
@Test
fun authenticateDoesNotRunIfKeyguardIsNotShowing() =
@@ -582,12 +601,6 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
testScope.runTest { testGatingCheckForFaceAuth { underTest.setLockedOut(true) } }
@Test
- fun authenticateDoesNotRunWhenUserIsCurrentlyTrusted() =
- testScope.runTest {
- testGatingCheckForFaceAuth { trustRepository.setCurrentUserTrusted(true) }
- }
-
- @Test
fun authenticateDoesNotRunWhenKeyguardIsGoingAway() =
testScope.runTest {
testGatingCheckForFaceAuth { keyguardRepository.setKeyguardGoingAway(true) }
@@ -608,14 +621,6 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
}
@Test
- fun authenticateDoesNotRunWhenFaceAuthIsNotCurrentlyAllowedToRun() =
- testScope.runTest {
- testGatingCheckForFaceAuth {
- biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
- }
- }
-
- @Test
fun authenticateDoesNotRunWhenSecureCameraIsActive() =
testScope.runTest {
testGatingCheckForFaceAuth {
@@ -672,7 +677,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
// Flip one precondition to false.
biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
assertThat(canFaceAuthRun()).isFalse()
- underTest.authenticate(
+ underTest.requestAuthenticate(
FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
fallbackToDetection = true
)
@@ -693,7 +698,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
trustRepository.setCurrentUserTrusted(true)
assertThat(canFaceAuthRun()).isFalse()
- underTest.authenticate(
+ underTest.requestAuthenticate(
FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
fallbackToDetection = true
)
@@ -884,10 +889,6 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
}
@Test
- fun detectDoesNotRunWhenUserSwitchingInProgress() =
- testScope.runTest { testGatingCheckForDetect { underTest.pauseFaceAuth() } }
-
- @Test
fun detectDoesNotRunWhenKeyguardGoingAway() =
testScope.runTest {
testGatingCheckForDetect { keyguardRepository.setKeyguardGoingAway(true) }
@@ -1075,6 +1076,28 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
faceAuthenticateIsCalled()
}
+ @Test
+ fun queuedAuthOnlyRequestShouldNotBeProcessedIfOnlyDetectionCanBeRun() =
+ testScope.runTest {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ // This will prevent auth from running but not detection
+ biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
+
+ runCurrent()
+ assertThat(canFaceAuthRun()).isFalse()
+
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, false)
+ runCurrent()
+
+ faceDetectIsNotCalled()
+ faceAuthenticateIsNotCalled()
+
+ biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
+ faceAuthenticateIsCalled()
+ }
+
private suspend fun TestScope.testGatingCheckForFaceAuth(
gatingCheckModifier: suspend () -> Unit
) {
@@ -1087,10 +1110,18 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
// gating check doesn't allow face auth to run.
assertThat(underTest.canRunFaceAuth.value).isFalse()
+ // request face auth just before gating conditions become true, this ensures any race
+ // conditions won't prevent face auth from running
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, false)
+ faceAuthenticateIsNotCalled()
+
// flip the gating check back on.
allPreconditionsToRunFaceAuthAreTrue()
+ assertThat(underTest.canRunFaceAuth.value).isTrue()
- triggerFaceAuth(false)
+ faceAuthenticateIsCalled()
+ assertThat(authRunning()).isTrue()
+ cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
// Flip gating check off
gatingCheckModifier()
@@ -1101,13 +1132,17 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
clearInvocations(faceManager)
// Try auth again
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+
+ runCurrent()
// Auth can't run again
faceAuthenticateIsNotCalled()
}
- private suspend fun TestScope.testGatingCheckForDetect(gatingCheckModifier: () -> Unit) {
+ private suspend fun TestScope.testGatingCheckForDetect(
+ gatingCheckModifier: suspend () -> Unit
+ ) {
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
@@ -1118,7 +1153,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
assertThat(canFaceAuthRun()).isFalse()
// Trigger authenticate with detection fallback
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetection = true)
+ underTest.requestAuthenticate(
+ FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
+ fallbackToDetection = true
+ )
+ runCurrent()
faceAuthenticateIsNotCalled()
faceDetectIsCalled()
@@ -1133,15 +1172,21 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
clearInvocations(faceManager)
// Try to run detect again
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetection = true)
+ underTest.requestAuthenticate(
+ FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
+ fallbackToDetection = true
+ )
// Detect won't run because preconditions are not true anymore.
faceDetectIsNotCalled()
}
- private suspend fun triggerFaceAuth(fallbackToDetect: Boolean) {
+ private fun TestScope.triggerFaceAuth(fallbackToDetect: Boolean) {
assertThat(canFaceAuthRun()).isTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetect)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetect)
+
+ runCurrent()
+
faceAuthenticateIsCalled()
assertThat(authRunning()).isTrue()
cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
@@ -1150,7 +1195,6 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue() {
verify(faceManager, atLeastOnce())
.addLockoutResetCallback(faceLockoutResetCallback.capture())
- underTest.resumeFaceAuth()
trustRepository.setCurrentUserTrusted(false)
keyguardRepository.setKeyguardGoingAway(false)
keyguardRepository.setWakefulnessModel(
@@ -1164,7 +1208,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true)
biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
biometricSettingsRepository.setIsUserInLockdown(false)
- fakeUserRepository.setSelectedUserInfo(primaryUser)
+ fakeUserRepository.setSelectedUserInfo(primaryUser, SelectionStatus.SELECTION_COMPLETE)
faceLockoutResetCallback.value.onLockoutReset(0)
bouncerRepository.setAlternateVisible(true)
keyguardRepository.setKeyguardShowing(true)
@@ -1187,7 +1231,9 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
private fun successResult() = FaceManager.AuthenticationResult(null, null, primaryUserId, false)
- private fun faceDetectIsCalled() {
+ private fun TestScope.faceDetectIsCalled() {
+ runCurrent()
+
verify(faceManager)
.detectFace(
cancellationSignal.capture(),
@@ -1196,7 +1242,9 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
)
}
- private fun faceAuthenticateIsCalled() {
+ private fun TestScope.faceAuthenticateIsCalled() {
+ runCurrent()
+
verify(faceManager)
.authenticate(
isNull(),
@@ -1207,7 +1255,9 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
)
}
- private fun faceAuthenticateIsNotCalled() {
+ private fun TestScope.faceAuthenticateIsNotCalled() {
+ runCurrent()
+
verify(faceManager, never())
.authenticate(
isNull(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index da70a9ff036f..2ed9de26dfa3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -224,23 +224,7 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
}
@Test
- fun faceAuthIsPausedWhenUserSwitchingIsInProgress() =
- testScope.runTest {
- underTest.start()
-
- fakeUserRepository.setSelectedUserInfo(primaryUser, SelectionStatus.SELECTION_COMPLETE)
- runCurrent()
- fakeUserRepository.setSelectedUserInfo(
- secondaryUser,
- SelectionStatus.SELECTION_IN_PROGRESS
- )
- runCurrent()
-
- assertThat(faceAuthRepository.isFaceAuthPaused()).isTrue()
- }
-
- @Test
- fun faceAuthIsUnpausedWhenUserSwitchingIsInComplete() =
+ fun faceAuthLockedOutStateIsUpdatedAfterUserSwitch() =
testScope.runTest {
underTest.start()
@@ -251,7 +235,6 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
SelectionStatus.SELECTION_IN_PROGRESS
)
runCurrent()
- assertThat(faceAuthRepository.isFaceAuthPaused()).isTrue()
bouncerRepository.setPrimaryShow(true)
// New user is not locked out.
@@ -262,7 +245,6 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
)
runCurrent()
- assertThat(faceAuthRepository.isFaceAuthPaused()).isFalse()
assertThat(faceAuthRepository.isLockedOut.value).isFalse()
runCurrent()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index 2b13dcabb640..322fb284dad7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -56,20 +56,7 @@ class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository {
_isLockedOut.value = isLockedOut
}
- private val faceAuthPaused = MutableStateFlow(false)
- override fun pauseFaceAuth() {
- faceAuthPaused.value = true
- }
-
- override fun resumeFaceAuth() {
- faceAuthPaused.value = false
- }
-
- fun isFaceAuthPaused(): Boolean {
- return faceAuthPaused.value
- }
-
- override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+ override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
_runningAuthRequest.value = uiEvent to fallbackToDetection
_isAuthRunning.value = true
}