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

Commit 61e3f4fb authored by Caitlin Shkuratov's avatar Caitlin Shkuratov
Browse files

Update isSecureCameraActive flow to be false after unlock.

The current implementation of `KeyguardInteractor.isSecureCameraActive`
correctly emits `false` when secure camera is closed and the user goes
back to lockscreen or primary bouncer. But, the flow then incorrectly
emits `true` once the device is unlocked. This is because
`isKeyguardVisible` and `isPrimaryBouncerShowing` are both false and the
last camera launch event is still POWER_DOUBLE_TAP.

This CL updates the flow to still emit `false` even after the device is
unlocked by making sure that the keyguard becoming visible or the
primary bouncer showing correctly reset the flow value.

Fixes: 373700726
Bug: 364360986
Flag: EXEMPT bugfix

New tests:
Test: Launch secure camera over lockscreen, then go back to lockscreen,
then unlock the device -> verify flow emits `false`
Test: Launch secure camera over lockscreen, then go back to lockscreen,
then launch the Home shortcut from lockscreen -> verify flow emits
`false`
Test: atest KeyguardInteractorTest

Tests that ensure face auth hasn't regressed:
Test: Have face auth running on lock screen, then launch secure camera
-> verify face auth stops immediately (circle disappears without
animation), verify front camera in camera app works without delay
Test: Launch secure camera over lockscreen, then tap on locked gallery
icon -> verify alternate bouncer shows, face auth runs
Test: Launch secure camera over lockscreen, then tap on locked gallery
icon, then swipe up to go to primary bouncer -> verify face auth runs

Change-Id: I6bd5c7e43e3662f6dc8c49f3ca132eb69178cfcb
parent fca8ee70
Loading
Loading
Loading
Loading
+51 −20
Original line number Diff line number Diff line
@@ -150,6 +150,53 @@ class KeyguardInteractorTest : SysuiTestCase() {
            assertThat(secureCameraActive()).isFalse()
        }

    /** Regression test for b/373700726. */
    @Test
    @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun testSecureCameraStillFalseAfterDeviceUnlocked() =
        testScope.runTest {
            val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
            runCurrent()

            // Launch camera
            underTest.onCameraLaunchDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
            assertThat(secureCameraActive()).isTrue()

            // Go back to keyguard
            repository.setKeyguardShowing(true)
            repository.setKeyguardOccluded(false)
            assertThat(secureCameraActive()).isFalse()

            // WHEN device is unlocked (and therefore keyguard is no longer showing)
            repository.setKeyguardShowing(false)

            // THEN we still show secure camera as *not* active
            assertThat(secureCameraActive()).isFalse()
        }

    /** Regression test for b/373700726. */
    @Test
    @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun testSecureCameraStillFalseAfterBouncerDismissed() =
        testScope.runTest {
            val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
            runCurrent()

            // Launch camera
            underTest.onCameraLaunchDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
            assertThat(secureCameraActive()).isTrue()

            // Show bouncer
            bouncerRepository.setPrimaryShow(true)
            assertThat(secureCameraActive()).isFalse()

            // WHEN device is unlocked (and therefore the bouncer is no longer showing)
            bouncerRepository.setPrimaryShow(false)

            // THEN we still show secure camera as *not* active
            assertThat(secureCameraActive()).isFalse()
        }

    @Test
    @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun keyguardVisibilityIsDefinedAsKeyguardShowingButNotOccluded() = runTest {
@@ -182,11 +229,7 @@ class KeyguardInteractorTest : SysuiTestCase() {
            val dismissAlpha by collectLastValue(underTest.dismissAlpha)
            assertThat(dismissAlpha).isEqualTo(1f)

            keyguardTransitionRepository.sendTransitionSteps(
                from = AOD,
                to = LOCKSCREEN,
                testScope,
            )
            keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope)

            repository.setStatusBarState(StatusBarState.KEYGUARD)
            // User begins to swipe up
@@ -208,11 +251,7 @@ class KeyguardInteractorTest : SysuiTestCase() {
            assertThat(dismissAlpha[0]).isEqualTo(1f)
            assertThat(dismissAlpha.size).isEqualTo(1)

            keyguardTransitionRepository.sendTransitionSteps(
                from = AOD,
                to = LOCKSCREEN,
                testScope,
            )
            keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope)

            // User begins to swipe up
            repository.setStatusBarState(StatusBarState.KEYGUARD)
@@ -328,11 +367,7 @@ class KeyguardInteractorTest : SysuiTestCase() {

            shadeRepository.setLegacyShadeExpansion(0f)

            keyguardTransitionRepository.sendTransitionSteps(
                from = AOD,
                to = LOCKSCREEN,
                testScope,
            )
            keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope)

            assertThat(keyguardTranslationY).isEqualTo(0f)
        }
@@ -350,11 +385,7 @@ class KeyguardInteractorTest : SysuiTestCase() {

            shadeRepository.setLegacyShadeExpansion(1f)

            keyguardTransitionRepository.sendTransitionSteps(
                from = AOD,
                to = LOCKSCREEN,
                testScope,
            )
            keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope)

            assertThat(keyguardTranslationY).isEqualTo(0f)
        }
+29 −10
Original line number Diff line number Diff line
@@ -297,20 +297,39 @@ constructor(
    val isKeyguardVisible: Flow<Boolean> =
        combine(isKeyguardShowing, isKeyguardOccluded) { showing, occluded -> showing && !occluded }

    /**
     * Event types that affect whether secure camera is active. Only used by [isSecureCameraActive].
     */
    private enum class SecureCameraRelatedEventType {
        KeyguardBecameVisible,
        PrimaryBouncerBecameVisible,
        SecureCameraLaunched,
    }

    /** Whether camera is launched over keyguard. */
    val isSecureCameraActive: Flow<Boolean> by lazy {
        combine(isKeyguardVisible, primaryBouncerShowing, onCameraLaunchDetected) {
                isKeyguardVisible,
                isPrimaryBouncerShowing,
                cameraLaunchEvent ->
                when {
                    isKeyguardVisible -> false
                    isPrimaryBouncerShowing -> false
                    else -> cameraLaunchEvent.type == CameraLaunchType.POWER_DOUBLE_TAP
    val isSecureCameraActive: Flow<Boolean> =
        merge(
                onCameraLaunchDetected
                    .filter { it.type == CameraLaunchType.POWER_DOUBLE_TAP }
                    .map { SecureCameraRelatedEventType.SecureCameraLaunched },
                isKeyguardVisible
                    .filter { it }
                    .map { SecureCameraRelatedEventType.KeyguardBecameVisible },
                primaryBouncerShowing
                    .filter { it }
                    .map { SecureCameraRelatedEventType.PrimaryBouncerBecameVisible },
            )
            .map {
                when (it) {
                    SecureCameraRelatedEventType.SecureCameraLaunched -> true
                    // When secure camera is closed, either the keyguard or the primary bouncer will
                    // have to show, so those events tell us that secure camera is no longer active.
                    SecureCameraRelatedEventType.KeyguardBecameVisible -> false
                    SecureCameraRelatedEventType.PrimaryBouncerBecameVisible -> false
                }
            }
            .onStart { emit(false) }
    }
            .distinctUntilChanged()

    /** The approximate location on the screen of the fingerprint sensor, if one is available. */
    val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation