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

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

[flexiglass] Minor tweaks to support Trust Managers.

The "trust manager" is a system that allows the user to select friendly
networks, devices, or locations, where their device can become more
relaxed and only automatically lock after 4 hours or when explicitly
locked using the power menu's "lockdown" option.

Since the trust system occurs in the upstream, Flexiglass didn't need to
make any changes to have it work with it. The device simply doesn't
enter locked state or enters lock state through the lockdown power menu
option and Flexiglass obeys.

The problem is with some transitions that Flexiglass didn't support:
1. Swiping up lockscreen while lockscreen is unlocked should dismiss the
   lockscreen (even if the auth method is not swipe). This supports
   dismissing the lockscreen from AOD if, ever needed (likely, this is
   no needed as the device must be woken up before user input can be
   processed)
2. (more important than #1), automatically dismiss the lockscreen and go
   to the Gone scene if the device wakes up while unlocked, regardless
   of which auth method is currently selected. This prevents the
   Flexiglass lockscreen scene UI from showing over the unlocked
   Launcher UI both while the device is unlocked due to the trust manager or due to the lock timeout not having elapsed yet.

Fix: 281730986
Fix: 294283030
Test: updated unit tests
Test: added new integration test cases
Test: manually verified that trust managers and lockdown mode both work
(see b/281730986#comment2 for details)
Test: manually verified the the screen off + screen on (within less time
than the lock timeout) scenario doesn't end up with a lockscreen over
the launcher.

Change-Id: Ia9d5b620d78c2ad7e47ceb218e7765bdf5eed52d
parent 1bc1d42d
Loading
Loading
Loading
Loading
+9 −9
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
@@ -51,15 +52,14 @@ constructor(
            )

    /** The key of the scene we should switch to when swiping up. */
    val upDestinationSceneKey =
        authenticationInteractor.canSwipeToDismiss
            .map { canSwipeToDismiss -> upDestinationSceneKey(canSwipeToDismiss) }
            .stateIn(
                scope = applicationScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue =
                    upDestinationSceneKey(authenticationInteractor.canSwipeToDismiss.value),
            )
    val upDestinationSceneKey: Flow<SceneKey> =
        authenticationInteractor.isUnlocked.map { isUnlocked ->
            if (isUnlocked) {
                SceneKey.Gone
            } else {
                SceneKey.Bouncer
            }
        }

    /** Notifies that the lock button on the lock screen was clicked. */
    fun onLockButtonClicked() {
+18 −6
Original line number Diff line number Diff line
@@ -178,13 +178,25 @@ constructor(
                        }
                        WakefulnessState.STARTING_TO_WAKE -> {
                            val authMethod = authenticationInteractor.getAuthenticationMethod()
                            if (authMethod == AuthenticationMethodModel.None) {
                            val isUnlocked = authenticationInteractor.isUnlocked.value
                            when {
                                authMethod == AuthenticationMethodModel.None -> {
                                    switchToScene(
                                        targetSceneKey = SceneKey.Gone,
                                        loggingReason =
                                        "device is starting to wake up while auth method is None",
                                            "device is starting to wake up while auth method is" +
                                                " none",
                                    )
                                }
                                authMethod.isSecure && isUnlocked -> {
                                    switchToScene(
                                        targetSceneKey = SceneKey.Gone,
                                        loggingReason =
                                            "device is starting to wake up while unlocked with a" +
                                                " secure auth method",
                                    )
                                }
                            }
                        }
                        else -> Unit
                    }
+32 −14
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ package com.android.systemui.scene

import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
@@ -31,6 +30,7 @@ import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.model.SysUiState
import com.android.systemui.scene.SceneTestUtils.Companion.toDataLayer
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
@@ -211,6 +211,17 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
            assertCurrentScene(SceneKey.Gone)
        }

    @Test
    fun withAuthMethodSwipe_deviceWakeUp_doesNotSkipLockscreen() =
        testScope.runTest {
            setAuthMethod(AuthenticationMethodModel.Swipe)
            putDeviceToSleep(instantlyLockDevice = false)
            assertCurrentScene(SceneKey.Lockscreen)

            wakeUpDevice()
            assertCurrentScene(SceneKey.Lockscreen)
        }

    @Test
    fun deviceGoesToSleep_switchesToLockscreen() =
        testScope.runTest {
@@ -235,6 +246,26 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
            assertCurrentScene(SceneKey.Gone)
        }

    @Test
    fun deviceWakesUpWhileUnlocked_dismissesLockscreen() =
        testScope.runTest {
            unlockDevice()
            assertCurrentScene(SceneKey.Gone)
            putDeviceToSleep(instantlyLockDevice = false)
            assertCurrentScene(SceneKey.Lockscreen)
            wakeUpDevice()
            assertCurrentScene(SceneKey.Gone)
        }

    @Test
    fun swipeUpOnLockscreenWhileUnlocked_dismissesLockscreen() =
        testScope.runTest {
            unlockDevice()
            val upDestinationSceneKey by
                collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
            assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Gone)
        }

    @Test
    fun deviceGoesToSleep_withLockTimeout_staysOnLockscreen() =
        testScope.runTest {
@@ -462,17 +493,4 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
            lockDevice()
        }
    }

    private fun DomainLayerAuthenticationMethodModel.toDataLayer():
        DataLayerAuthenticationMethodModel {
        return when (this) {
            DomainLayerAuthenticationMethodModel.None -> DataLayerAuthenticationMethodModel.None
            DomainLayerAuthenticationMethodModel.Swipe -> DataLayerAuthenticationMethodModel.None
            DomainLayerAuthenticationMethodModel.Pin -> DataLayerAuthenticationMethodModel.Pin
            DomainLayerAuthenticationMethodModel.Password ->
                DataLayerAuthenticationMethodModel.Password
            DomainLayerAuthenticationMethodModel.Pattern ->
                DataLayerAuthenticationMethodModel.Pattern
        }
    }
}
+34 −125
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@ package com.android.systemui.scene.domain.startable
import android.view.Display
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.WakeSleepReason
@@ -29,6 +29,7 @@ import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.model.SysUiState
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.SceneTestUtils.Companion.toDataLayer
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -80,14 +81,13 @@ class SceneContainerStartableTest : SysuiTestCase() {
        )

    @Test
    fun hydrateVisibility_featureEnabled() =
    fun hydrateVisibility() =
        testScope.runTest {
            val currentDesiredSceneKey by
                collectLastValue(sceneInteractor.desiredScene.map { it.key })
            val isVisible by collectLastValue(sceneInteractor.isVisible)
            val transitionStateFlow =
                prepareState(
                    isFeatureEnabled = true,
                    isDeviceUnlocked = true,
                    initialSceneKey = SceneKey.Gone,
                )
@@ -123,44 +123,10 @@ class SceneContainerStartableTest : SysuiTestCase() {
        }

    @Test
    fun hydrateVisibility_featureDisabled() =
        testScope.runTest {
            val currentDesiredSceneKey by
                collectLastValue(sceneInteractor.desiredScene.map { it.key })
            val isVisible by collectLastValue(sceneInteractor.isVisible)
            val transitionStateFlow =
                prepareState(
                    isFeatureEnabled = false,
                    isDeviceUnlocked = true,
                    initialSceneKey = SceneKey.Gone,
                )
            assertThat(currentDesiredSceneKey).isEqualTo(SceneKey.Gone)
            assertThat(isVisible).isTrue()

            underTest.start()

            assertThat(isVisible).isTrue()

            sceneInteractor.changeScene(SceneModel(SceneKey.Shade), "reason")
            transitionStateFlow.value =
                ObservableTransitionState.Transition(
                    fromScene = SceneKey.Gone,
                    toScene = SceneKey.Shade,
                    progress = flowOf(0.5f),
                )
            assertThat(isVisible).isTrue()

            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
            transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Shade)
            assertThat(isVisible).isTrue()
        }

    @Test
    fun switchToLockscreenWhenDeviceLocks_featureEnabled() =
    fun switchToLockscreenWhenDeviceLocks() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
            prepareState(
                isFeatureEnabled = true,
                isDeviceUnlocked = true,
                initialSceneKey = SceneKey.Gone,
            )
@@ -173,28 +139,10 @@ class SceneContainerStartableTest : SysuiTestCase() {
        }

    @Test
    fun switchToLockscreenWhenDeviceLocks_featureDisabled() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
            prepareState(
                isFeatureEnabled = false,
                isDeviceUnlocked = false,
                initialSceneKey = SceneKey.Gone,
            )
            assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
            underTest.start()

            authenticationRepository.setUnlocked(false)

            assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
        }

    @Test
    fun switchFromBouncerToGoneWhenDeviceUnlocked_featureEnabled() =
    fun switchFromBouncerToGoneWhenDeviceUnlocked() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
            prepareState(
                isFeatureEnabled = true,
                isDeviceUnlocked = false,
                initialSceneKey = SceneKey.Bouncer,
            )
@@ -207,28 +155,10 @@ class SceneContainerStartableTest : SysuiTestCase() {
        }

    @Test
    fun switchFromBouncerToGoneWhenDeviceUnlocked_featureDisabled() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
            prepareState(
                isFeatureEnabled = false,
                isDeviceUnlocked = false,
                initialSceneKey = SceneKey.Bouncer,
            )
            assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
            underTest.start()

            authenticationRepository.setUnlocked(true)

            assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
        }

    @Test
    fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOn() =
    fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
            prepareState(
                isFeatureEnabled = true,
                isBypassEnabled = true,
                initialSceneKey = SceneKey.Lockscreen,
            )
@@ -241,11 +171,10 @@ class SceneContainerStartableTest : SysuiTestCase() {
        }

    @Test
    fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOff() =
    fun stayOnLockscreenWhenDeviceUnlocksWithBypassOff() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
            prepareState(
                isFeatureEnabled = true,
                isBypassEnabled = false,
                initialSceneKey = SceneKey.Lockscreen,
            )
@@ -258,28 +187,10 @@ class SceneContainerStartableTest : SysuiTestCase() {
        }

    @Test
    fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOff_bypassOn() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
            prepareState(
                isFeatureEnabled = false,
                isBypassEnabled = true,
                initialSceneKey = SceneKey.Lockscreen,
            )
            assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
            underTest.start()

            authenticationRepository.setUnlocked(true)

            assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
        }

    @Test
    fun switchToLockscreenWhenDeviceSleepsLocked_featureEnabled() =
    fun switchToLockscreenWhenDeviceSleepsLocked() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
            prepareState(
                isFeatureEnabled = true,
                isDeviceUnlocked = false,
                initialSceneKey = SceneKey.Shade,
            )
@@ -291,23 +202,6 @@ class SceneContainerStartableTest : SysuiTestCase() {
            assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
        }

    @Test
    fun switchToLockscreenWhenDeviceSleepsLocked_featureDisabled() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
            prepareState(
                isFeatureEnabled = false,
                isDeviceUnlocked = false,
                initialSceneKey = SceneKey.Shade,
            )
            assertThat(currentSceneKey).isEqualTo(SceneKey.Shade)
            underTest.start()

            keyguardRepository.setWakefulnessModel(STARTING_TO_SLEEP)

            assertThat(currentSceneKey).isEqualTo(SceneKey.Shade)
        }

    @Test
    fun hydrateSystemUiState() =
        testScope.runTest {
@@ -339,11 +233,10 @@ class SceneContainerStartableTest : SysuiTestCase() {
        }

    @Test
    fun switchToGoneWhenDeviceStartsToWakeUp_authMethodNone_featureEnabled() =
    fun switchToGoneWhenDeviceStartsToWakeUp_authMethodNone() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
            prepareState(
                isFeatureEnabled = true,
                initialSceneKey = SceneKey.Lockscreen,
                authenticationMethod = AuthenticationMethodModel.None,
            )
@@ -356,13 +249,12 @@ class SceneContainerStartableTest : SysuiTestCase() {
        }

    @Test
    fun switchToGoneWhenDeviceStartsToWakeUp_authMethodNotNone_featureEnabled() =
    fun stayOnLockscreenWhenDeviceStartsToWakeUp_authMethodSwipe() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
            prepareState(
                isFeatureEnabled = true,
                initialSceneKey = SceneKey.Lockscreen,
                authenticationMethod = AuthenticationMethodModel.Pin,
                authenticationMethod = AuthenticationMethodModel.Swipe,
            )
            assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
            underTest.start()
@@ -373,13 +265,12 @@ class SceneContainerStartableTest : SysuiTestCase() {
        }

    @Test
    fun switchToGoneWhenDeviceStartsToWakeUp_authMethodNone_featureDisabled() =
    fun doesNotSwitchToGoneWhenDeviceStartsToWakeUp_authMethodSecure() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
            prepareState(
                isFeatureEnabled = false,
                initialSceneKey = SceneKey.Lockscreen,
                authenticationMethod = AuthenticationMethodModel.None,
                authenticationMethod = AuthenticationMethodModel.Pin,
            )
            assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
            underTest.start()
@@ -389,14 +280,32 @@ class SceneContainerStartableTest : SysuiTestCase() {
            assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
        }

    @Test
    fun switchToGoneWhenDeviceStartsToWakeUp_authMethodSecure_deviceUnlocked() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
            prepareState(
                initialSceneKey = SceneKey.Lockscreen,
                authenticationMethod = AuthenticationMethodModel.Pin,
                isDeviceUnlocked = false,
            )
            assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
            underTest.start()

            authenticationRepository.setUnlocked(true)
            runCurrent()
            keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE)

            assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
        }

    private fun prepareState(
        isFeatureEnabled: Boolean = true,
        isDeviceUnlocked: Boolean = false,
        isBypassEnabled: Boolean = false,
        initialSceneKey: SceneKey? = null,
        authenticationMethod: AuthenticationMethodModel? = null,
    ): MutableStateFlow<ObservableTransitionState> {
        featureFlags.set(Flags.SCENE_CONTAINER, isFeatureEnabled)
        featureFlags.set(Flags.SCENE_CONTAINER, true)
        authenticationRepository.setUnlocked(isDeviceUnlocked)
        keyguardRepository.setBypassEnabled(isBypassEnabled)
        val transitionStateFlow =
@@ -410,7 +319,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
            sceneInteractor.onSceneChanged(SceneModel(it), "reason")
        }
        authenticationMethod?.let {
            authenticationRepository.setAuthenticationMethod(authenticationMethod)
            authenticationRepository.setAuthenticationMethod(authenticationMethod.toDataLayer())
            authenticationRepository.setLockscreenEnabled(
                authenticationMethod != AuthenticationMethodModel.None
            )
+15 −0
Original line number Diff line number Diff line
@@ -18,9 +18,11 @@ package com.android.systemui.scene

import android.content.pm.UserInfo
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
import com.android.systemui.authentication.data.repository.AuthenticationRepository
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
import com.android.systemui.bouncer.data.repository.BouncerRepository
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
@@ -201,5 +203,18 @@ class SceneTestUtils(
                RemoteUserInput(10f, 40f, RemoteUserInputAction.MOVE),
                RemoteUserInput(10f, 40f, RemoteUserInputAction.UP),
            )

        fun DomainLayerAuthenticationMethodModel.toDataLayer(): DataLayerAuthenticationMethodModel {
            return when (this) {
                DomainLayerAuthenticationMethodModel.None -> DataLayerAuthenticationMethodModel.None
                DomainLayerAuthenticationMethodModel.Swipe ->
                    DataLayerAuthenticationMethodModel.None
                DomainLayerAuthenticationMethodModel.Pin -> DataLayerAuthenticationMethodModel.Pin
                DomainLayerAuthenticationMethodModel.Password ->
                    DataLayerAuthenticationMethodModel.Password
                DomainLayerAuthenticationMethodModel.Pattern ->
                    DataLayerAuthenticationMethodModel.Pattern
            }
        }
    }
}