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

Commit c6ffe1dc authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[flexiglass] Adds support for locking when entering dream state

Fix: 377335319
Test: manually verified that entering dream state locks the device with
the appropriate timeout and that exiting it before the timeout elapses
doesn't lock it
Test: unit tests added
Flag: com.android.systemui.scene_container

Change-Id: Ia52d4a67d799cb7375dbe7674ceb467983edcf17
parent 3aab3c96
Loading
Loading
Loading
Loading
+100 −20
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import com.android.systemui.flags.fakeSystemPropertiesHelper
import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeTrustRepository
import com.android.systemui.keyguard.shared.model.AuthenticationFlags
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
@@ -40,6 +41,8 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.fakeUserRepository
@@ -47,6 +50,7 @@ import com.android.systemui.user.domain.interactor.selectedUserInteractor
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
@@ -230,11 +234,7 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() {
    @Test
    fun deviceUnlockStatus_isResetToFalse_whenDeviceGoesToSleep() =
        testScope.runTest {
            kosmos.fakeSettings.putIntForUser(
                Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
                0,
                kosmos.selectedUserInteractor.getSelectedUserId(),
            )
            setLockAfterScreenTimeout(0)
            kosmos.fakeAuthenticationRepository.powerButtonInstantlyLocks = false
            val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)

@@ -254,11 +254,7 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() {
    fun deviceUnlockStatus_isResetToFalse_whenDeviceGoesToSleep_afterDelay() =
        testScope.runTest {
            val delay = 5000
            kosmos.fakeSettings.putIntForUser(
                Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
                delay,
                kosmos.selectedUserInteractor.getSelectedUserId(),
            )
            setLockAfterScreenTimeout(delay)
            kosmos.fakeAuthenticationRepository.powerButtonInstantlyLocks = false
            val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)

@@ -279,11 +275,7 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() {
    @Test
    fun deviceUnlockStatus_isResetToFalse_whenDeviceGoesToSleep_powerButtonLocksInstantly() =
        testScope.runTest {
            kosmos.fakeSettings.putIntForUser(
                Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
                5000,
                kosmos.selectedUserInteractor.getSelectedUserId(),
            )
            setLockAfterScreenTimeout(5000)
            kosmos.fakeAuthenticationRepository.powerButtonInstantlyLocks = true
            val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)

@@ -304,11 +296,7 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() {
    @Test
    fun deviceUnlockStatus_becomesUnlocked_whenFingerprintUnlocked_whileDeviceAsleep() =
        testScope.runTest {
            kosmos.fakeSettings.putIntForUser(
                Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
                0,
                kosmos.selectedUserInteractor.getSelectedUserId(),
            )
            setLockAfterScreenTimeout(0)
            val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)
            assertThat(deviceUnlockStatus?.isUnlocked).isFalse()

@@ -517,6 +505,98 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() {
                .isEqualTo(DeviceEntryRestrictionReason.DeviceNotUnlockedSinceMainlineUpdate)
        }

    @Test
    fun deviceUnlockStatus_locksImmediately_whenDreamStarts_noTimeout() =
        testScope.runTest {
            setLockAfterScreenTimeout(0)
            val isUnlocked by collectLastValue(underTest.deviceUnlockStatus.map { it.isUnlocked })
            unlockDevice()

            startDreaming()

            assertThat(isUnlocked).isFalse()
        }

    @Test
    fun deviceUnlockStatus_locksWithDelay_afterDreamStarts_withTimeout() =
        testScope.runTest {
            val delay = 5000
            setLockAfterScreenTimeout(delay)
            val isUnlocked by collectLastValue(underTest.deviceUnlockStatus.map { it.isUnlocked })
            unlockDevice()

            startDreaming()
            assertThat(isUnlocked).isTrue()

            advanceTimeBy(delay - 1L)
            assertThat(isUnlocked).isTrue()

            advanceTimeBy(1L)
            assertThat(isUnlocked).isFalse()
        }

    @Test
    fun deviceUnlockStatus_doesNotLockWithDelay_whenDreamStopsBeforeTimeout() =
        testScope.runTest {
            val delay = 5000
            setLockAfterScreenTimeout(delay)
            val isUnlocked by collectLastValue(underTest.deviceUnlockStatus.map { it.isUnlocked })
            unlockDevice()

            startDreaming()
            assertThat(isUnlocked).isTrue()

            advanceTimeBy(delay - 1L)
            assertThat(isUnlocked).isTrue()

            stopDreaming()
            assertThat(isUnlocked).isTrue()

            advanceTimeBy(1L)
            assertThat(isUnlocked).isTrue()
        }

    @Test
    fun deviceUnlockStatus_doesNotLock_whenDreamStarts_ifNotInteractive() =
        testScope.runTest {
            setLockAfterScreenTimeout(0)
            val isUnlocked by collectLastValue(underTest.deviceUnlockStatus.map { it.isUnlocked })
            unlockDevice()

            startDreaming()

            assertThat(isUnlocked).isFalse()
        }

    private fun TestScope.unlockDevice() {
        val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)

        kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
            SuccessFingerprintAuthenticationStatus(0, true)
        )
        assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
        kosmos.sceneInteractor.changeScene(Scenes.Gone, "reason")
        runCurrent()
    }

    private fun setLockAfterScreenTimeout(timeoutMs: Int) {
        kosmos.fakeSettings.putIntForUser(
            Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
            timeoutMs,
            kosmos.selectedUserInteractor.getSelectedUserId(),
        )
    }

    private fun TestScope.startDreaming() {
        kosmos.fakeKeyguardRepository.setDreaming(true)
        runCurrent()
    }

    private fun TestScope.stopDreaming() {
        kosmos.fakeKeyguardRepository.setDreaming(false)
        runCurrent()
    }

    private fun TestScope.verifyRestrictionReasonsForAuthFlags(
        vararg authFlagToDeviceEntryRestriction: Pair<Int, DeviceEntryRestrictionReason?>
    ) {
+19 −0
Original line number Diff line number Diff line
@@ -30,11 +30,13 @@ import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource
import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus
import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.TrustInteractor
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import javax.inject.Inject
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
@@ -49,6 +51,7 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
@@ -69,6 +72,7 @@ constructor(
    private val biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
    private val systemPropertiesHelper: SystemPropertiesHelper,
    private val userAwareSecureSettingsRepository: UserAwareSecureSettingsRepository,
    private val keyguardInteractor: KeyguardInteractor,
) : ExclusiveActivatable() {

    private val deviceUnlockSource =
@@ -236,6 +240,21 @@ constructor(
                    .distinctUntilChanged()
                    .filter { it }
                    .map { LockImmediately("lockdown") },
                // Started dreaming
                powerInteractor.isInteractive.flatMapLatestConflated { isInteractive ->
                    // Only respond to dream state changes while the device is interactive.
                    if (isInteractive) {
                        keyguardInteractor.isDreamingAny.distinctUntilChanged().map { isDreaming ->
                            if (isDreaming) {
                                LockWithDelay("started dreaming")
                            } else {
                                CancelDelayedLock("stopped dreaming")
                            }
                        }
                    } else {
                        emptyFlow()
                    }
                },
            )
            .collectLatest(::onLockEvent)
    }
+2 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.deviceentry.domain.interactor
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.deviceentry.data.repository.deviceEntryRepository
import com.android.systemui.flags.fakeSystemPropertiesHelper
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.trustInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -38,6 +39,7 @@ val Kosmos.deviceUnlockedInteractor by Fixture {
            biometricSettingsInteractor = deviceEntryBiometricSettingsInteractor,
            systemPropertiesHelper = fakeSystemPropertiesHelper,
            userAwareSecureSettingsRepository = userAwareSecureSettingsRepository,
            keyguardInteractor = keyguardInteractor,
        )
        .apply { activateIn(testScope) }
}