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

Commit 6d147ed5 authored by Grace Cheng's avatar Grace Cheng
Browse files

Fix biometric auth visibility + bouncer message suppression

Fix biometric auth visibility + bouncer message suppression after secure lock device disabled

Flag: android.security.secure_lock_device
Test: atest SecureLockDeviceBiometricAuthContentViewModelTest
Test: atest SecureLockDeviceInteractorTest
Bug: 401645997
Change-Id: I75eccd8855bd215f0b4d26a80c30c7e4aca82073
parent 2c8cd7e0
Loading
Loading
Loading
Loading
+100 −0
Original line number Diff line number Diff line
@@ -215,4 +215,104 @@ class SecureLockDeviceInteractorTest : SysuiTestCase() {
            assertThat(shouldListenForBiometricAuth).isFalse()
        }
    }

    @Test
    fun onBiometricAuthRequested_resetsStateAndShowsBiometricAuthUi() =
        testScope.runTest {
            val isBiometricAuthVisible by collectLastValue(underTest.isBiometricAuthVisible)
            val showConfirmBiometricAuthButton by
                collectLastValue(underTest.showConfirmBiometricAuthButton)
            val showTryAgainButton by collectLastValue(underTest.showTryAgainButton)
            val showingError by collectLastValue(underTest.showingError)
            val suppressBouncerMessageUpdates by
                collectLastValue(underTest.suppressBouncerMessageUpdates)

            underTest.onBiometricAuthRequested()
            runCurrent()

            assertThat(isBiometricAuthVisible).isTrue()
            assertThat(showConfirmBiometricAuthButton).isFalse()
            assertThat(showTryAgainButton).isFalse()
            assertThat(showingError).isFalse()
            assertThat(suppressBouncerMessageUpdates).isFalse()
        }

    @Test
    fun onBiometricAuthUiHiddenWithoutAuthenticationComplete_resetsUIState() =
        testScope.runTest {
            val isBiometricAuthVisible by collectLastValue(underTest.isBiometricAuthVisible)
            val showConfirmBiometricAuthButton by
                collectLastValue(underTest.showConfirmBiometricAuthButton)
            val showTryAgainButton by collectLastValue(underTest.showTryAgainButton)
            val showingError by collectLastValue(underTest.showingError)
            val suppressBouncerMessageUpdates by
                collectLastValue(underTest.suppressBouncerMessageUpdates)

            // Sample state, pending face auth confirmation, suppressing bouncer message updates
            underTest.onBiometricAuthRequested()
            underTest.onBiometricAuthenticatedStateUpdated(
                PromptAuthState(
                    isAuthenticated = true,
                    authenticatedModality = BiometricModality.Face,
                    needsUserConfirmation = true,
                )
            )
            underTest.suppressBouncerMessages()
            runCurrent()

            assertThat(isBiometricAuthVisible).isTrue()
            assertThat(showConfirmBiometricAuthButton).isTrue()
            assertThat(showingError).isFalse()
            assertThat(suppressBouncerMessageUpdates).isTrue()

            // Biometric auth UI is hidden without confirmation (e.g. screen turned off)
            underTest.onBiometricAuthUiHidden()
            runCurrent()

            assertThat(isBiometricAuthVisible).isFalse()
            assertThat(showConfirmBiometricAuthButton).isFalse()
            assertThat(showTryAgainButton).isFalse()
            assertThat(showingError).isFalse()
            assertThat(suppressBouncerMessageUpdates).isFalse()
        }

    @Test
    fun onGoneTransitionFinished_resetsUIState() =
        testScope.runTest {
            val isBiometricAuthVisible by collectLastValue(underTest.isBiometricAuthVisible)
            val isFullyUnlockedAndReadyToDismiss by
                collectLastValue(underTest.isFullyUnlockedAndReadyToDismiss)
            val showConfirmBiometricAuthButton by
                collectLastValue(underTest.showConfirmBiometricAuthButton)
            val showTryAgainButton by collectLastValue(underTest.showTryAgainButton)
            val showingError by collectLastValue(underTest.showingError)
            val suppressBouncerMessageUpdates by
                collectLastValue(underTest.suppressBouncerMessageUpdates)

            // Authentication complete, animations played, ready to dismiss
            underTest.onBiometricAuthRequested()
            underTest.onBiometricAuthenticatedStateUpdated(
                PromptAuthState(
                    isAuthenticated = true,
                    authenticatedModality = BiometricModality.Face,
                    needsUserConfirmation = true,
                )
            )
            underTest.suppressBouncerMessages()
            underTest.onReadyToDismissBiometricAuth()
            runCurrent()

            assertThat(isBiometricAuthVisible).isTrue()
            assertThat(isFullyUnlockedAndReadyToDismiss).isTrue()

            underTest.onGoneTransitionFinished()
            runCurrent()

            assertThat(isBiometricAuthVisible).isFalse()
            assertThat(isFullyUnlockedAndReadyToDismiss).isFalse()
            assertThat(showConfirmBiometricAuthButton).isFalse()
            assertThat(showTryAgainButton).isFalse()
            assertThat(showingError).isFalse()
            assertThat(suppressBouncerMessageUpdates).isFalse()
        }
}
+13 −7
Original line number Diff line number Diff line
@@ -305,7 +305,7 @@ constructor(
    /** Called when biometric authentication is requested for secure lock device. */
    fun onBiometricAuthRequested() {
        logBuffer.log(TAG, LogLevel.DEBUG, "onBiometricAuthRequested")
        resetBiometricAuthState()
        resetBiometricAuthState(isBiometricAuthRequested = true)
        _isFullyUnlockedAndReadyToDismiss.value = false
        _isBiometricAuthVisible.value = true
        deviceEntryFaceAuthInteractor.onSecureLockDeviceBiometricAuthRequested()
@@ -324,10 +324,16 @@ constructor(
     * Resets UI state when leaving the biometric auth screen without authenticating, or when the
     * secure lock device UI state is reset upon the gone transition completing.
     */
    private fun resetBiometricAuthState() {
        logBuffer.log(TAG, LogLevel.DEBUG, "resetBiometricAuthState")
        secureLockDeviceRepository.suppressBouncerMessageUpdates.value = true
    private fun resetBiometricAuthState(isBiometricAuthRequested: Boolean) {
        logBuffer.log(
            TAG,
            LogLevel.DEBUG,
            "resetBiometricAuthState(isBiometricAuthRequested $isBiometricAuthRequested)",
        )
        secureLockDeviceRepository.suppressBouncerMessageUpdates.value = false
        if (!isBiometricAuthRequested) {
            _isBiometricAuthVisible.value = false
        }
        _showConfirmBiometricAuthButton.value = false
        _showTryAgainButton.value = false
        _showingError.value = false
@@ -360,7 +366,7 @@ constructor(
                PRIMARY_AUTH_REQUIRED_FOR_SECURE_LOCK_DEVICE,
                selectedUserInteractor.getSelectedUserId(),
            )
            resetBiometricAuthState()
            resetBiometricAuthState(isBiometricAuthRequested = false)
        }
    }

@@ -376,7 +382,7 @@ constructor(
                    "biometric authentication"
            )
        )
        resetBiometricAuthState()
        resetBiometricAuthState(isBiometricAuthRequested = false)
        _isFullyUnlockedAndReadyToDismiss.value = false
        _strongBiometricAuthenticationComplete.value = false
    }