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

Commit e5f8bcaa authored by Beverly Tai's avatar Beverly Tai Committed by Android (Google) Code Review
Browse files

Merge "Run face detect when face is locked out & show scanning anim" into main

parents d5b71032 0db1775e
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -155,6 +155,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
    private lateinit var authStatus: FlowValue<FaceAuthenticationStatus?>
    private lateinit var detectStatus: FlowValue<FaceDetectionStatus?>
    private lateinit var authRunning: FlowValue<Boolean?>
    private lateinit var detectRunning: FlowValue<Boolean?>
    private lateinit var bypassEnabled: FlowValue<Boolean?>
    private lateinit var lockedOut: FlowValue<Boolean?>
    private lateinit var canFaceAuthRun: FlowValue<Boolean?>
@@ -377,6 +378,38 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
                .isEqualTo(AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED)
        }

    @Test
    fun faceDetectionRunsAndSucceeds_detectRunningStateUpdates() =
        testScope.runTest {
            whenever(faceManager.sensorPropertiesInternal)
                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
            underTest = createDeviceEntryFaceAuthRepositoryImpl()
            initCollectors()

            underTest.detect(FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED)
            faceDetectIsCalled()
            assertThat(detectRunning()).isTrue()

            detectionCallback.value.onFaceDetected(1, 1, true)
            assertThat(detectRunning()).isFalse()
        }

    @Test
    fun faceDetectionRunsAndCancels_detectRunningStateUpdates() =
        testScope.runTest {
            whenever(faceManager.sensorPropertiesInternal)
                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
            underTest = createDeviceEntryFaceAuthRepositoryImpl()
            initCollectors()

            underTest.detect(FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED)
            faceDetectIsCalled()
            assertThat(detectRunning()).isTrue()

            underTest.cancel()
            assertThat(detectRunning()).isFalse()
        }

    @Test
    fun faceDetectDoesNotRunIfDetectionIsNotSupported() =
        testScope.runTest {
@@ -390,6 +423,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {

            verify(faceManager, never())
                .detectFace(any(), any(), any(FaceAuthenticateOptions::class.java))
            assertThat(detectRunning()).isFalse()
        }

    @Test
@@ -786,6 +820,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
            assertThat(authStatus()).isNull()
            assertThat(detectStatus()).isNull()
            assertThat(authRunning()).isNotNull()
            assertThat(detectRunning()).isNotNull()
            assertThat(bypassEnabled()).isNotNull()
            assertThat(lockedOut()).isNotNull()
            assertThat(canFaceAuthRun()).isNotNull()
@@ -1388,6 +1423,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
        authStatus = collectLastValue(underTest.authenticationStatus)
        detectStatus = collectLastValue(underTest.detectionStatus)
        authRunning = collectLastValue(underTest.isAuthRunning)
        detectRunning = collectLastValue(underTest.isDetectRunning)
        lockedOut = collectLastValue(underTest.isLockedOut)
        canFaceAuthRun = collectLastValue(underTest.canRunFaceAuth)
        authenticated = collectLastValue(underTest.isAuthenticated)
+15 −1
Original line number Diff line number Diff line
@@ -189,11 +189,12 @@ class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() {
        }

    @Test
    fun whenFaceIsLockedOutAnyAttemptsToTriggerFaceAuthMustProvideLockoutError() =
    fun whenFaceIsLockedOutAndNonBypassAnyAttemptsToTriggerFaceAuthMustProvideLockoutError() =
        testScope.runTest {
            underTest.start()
            val authenticationStatus = collectLastValue(underTest.authenticationStatus)
            faceAuthRepository.setLockedOut(true)
            kosmos.fakeDeviceEntryFaceAuthRepository.isBypassEnabled.value = false

            underTest.onDeviceLifted()

@@ -204,6 +205,19 @@ class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() {
            assertThat(faceAuthRepository.runningAuthRequest.value).isNull()
        }

    @Test
    fun whenFaceIsLockedOutAndBypass_DetectRuns() =
        testScope.runTest {
            underTest.start()
            val authenticationStatus = collectLastValue(underTest.authenticationStatus)
            faceAuthRepository.setLockedOut(true)
            kosmos.fakeDeviceEntryFaceAuthRepository.isBypassEnabled.value = true

            underTest.onDeviceLifted()

            assertThat(faceAuthRepository.runningAuthRequest.value).isNotNull()
        }

    @Test
    fun faceAuthIsRequestedWhenLockscreenBecomesVisibleFromAodState() =
        testScope.runTest {
+3 −1
Original line number Diff line number Diff line
@@ -1310,7 +1310,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, CoreSt
     */
    @Deprecated
    public boolean isFaceDetectionRunning() {
        return getFaceAuthInteractor() != null && getFaceAuthInteractor().isRunning();
        return getFaceAuthInteractor() != null
                && (getFaceAuthInteractor().isAuthRunning()
                || getFaceAuthInteractor().isDetectRunning());
    }

    private @Nullable DeviceEntryFaceAuthInteractor getFaceAuthInteractor() {
+39 −23
Original line number Diff line number Diff line
@@ -113,8 +113,11 @@ interface DeviceEntryFaceAuthRepository {
    /** Current state of whether face authentication is running. */
    val isAuthRunning: StateFlow<Boolean>

    /** Current state of whether face detection is running. */
    val isDetectRunning: StateFlow<Boolean>

    /** Whether bypass is currently enabled */
    val isBypassEnabled: Flow<Boolean>
    val isBypassEnabled: StateFlow<Boolean>

    /** Set whether face authentication should be locked out or not */
    fun setLockedOut(isLockedOut: Boolean)
@@ -197,6 +200,9 @@ constructor(
    override val isAuthRunning: StateFlow<Boolean>
        get() = _isAuthRunning

    private val _isDetectRunning = MutableStateFlow(false)
    override val isDetectRunning: StateFlow<Boolean> = _isDetectRunning

    private val keyguardSessionId: InstanceId?
        get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)

@@ -209,8 +215,8 @@ constructor(

    private var cancellationInProgress = MutableStateFlow(false)

    override val isBypassEnabled: Flow<Boolean> =
        keyguardBypassController?.let {
    override val isBypassEnabled: StateFlow<Boolean> =
        (keyguardBypassController?.let {
                conflatedCallbackFlow {
                    val callback =
                        object : KeyguardBypassController.OnBypassStateChangedListener {
@@ -222,7 +228,12 @@ constructor(
                    trySendWithFailureLogging(it.bypassEnabled, TAG, "BypassStateChanged")
                    awaitClose { it.unregisterOnBypassStateChangedListener(callback) }
                }
        } ?: flowOf(false)
            } ?: flowOf(false))
            .stateIn(
                scope = applicationScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = keyguardBypassController?.isBypassEnabled() ?: false,
            )

    override fun setLockedOut(isLockedOut: Boolean) {
        _isLockedOut.value = isLockedOut
@@ -397,14 +408,15 @@ constructor(
                        .map { it.isTransitioning(to = Scenes.Gone) || it.isIdle(Scenes.Gone) }
                        .isFalse()
                } else {
                    (keyguardTransitionInteractor.isFinishedIn(KeyguardState.GONE)
                    (keyguardTransitionInteractor
                            .isFinishedIn(KeyguardState.GONE)
                            .or(
                                keyguardTransitionInteractor.isInTransition(
                                    Edge.create(to = Scenes.Gone),
                                Edge.create(to = KeyguardState.GONE)
                                    Edge.create(to = KeyguardState.GONE),
                                )
                        )
                    ).isFalse()
                            ))
                        .isFalse()
                },
                "keyguardNotGoneOrTransitioningToGone",
            ),
@@ -552,6 +564,7 @@ constructor(

    private val detectionCallback =
        FaceManager.FaceDetectionCallback { sensorId, userId, isStrong ->
            _isDetectRunning.value = false
            faceAuthLogger.faceDetected()
            _detectionStatus.value = FaceDetectionStatus(sensorId, userId, isStrong)
        }
@@ -669,6 +682,7 @@ constructor(
            return
        }
        withContext(mainDispatcher) {
            _isDetectRunning.value = true
            // We always want to invoke face detect in the main thread.
            faceAuthLogger.faceDetectionStarted()
            detectCancellationSignal?.cancel()
@@ -688,12 +702,13 @@ constructor(
        get() = userRepository.getSelectedUserInfo().id

    private fun cancelDetection() {
        _isDetectRunning.value = false
        detectCancellationSignal?.cancel()
        detectCancellationSignal = null
    }

    override fun cancel() {
        if (authCancellationSignal == null) return
        if (authCancellationSignal == null && detectCancellationSignal == null) return

        authCancellationSignal?.cancel()
        cancelNotReceivedHandlerJob?.cancel()
@@ -711,6 +726,7 @@ constructor(
            }
        cancellationInProgress.value = true
        _isAuthRunning.value = false
        _isDetectRunning.value = false
    }

    companion object {
+4 −2
Original line number Diff line number Diff line
@@ -49,8 +49,10 @@ class NoopDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceA

    override val isAuthRunning: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow()

    override val isBypassEnabled: Flow<Boolean>
        get() = emptyFlow()
    override val isDetectRunning: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow()

    override val isBypassEnabled: StateFlow<Boolean>
        get() = MutableStateFlow(false)

    override fun setLockedOut(isLockedOut: Boolean) = Unit

Loading