diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt index 11e85d0d85e16..6d3b7f18e9745 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt @@ -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 = + authenticationInteractor.isUnlocked.map { isUnlocked -> + if (isUnlocked) { + SceneKey.Gone + } else { + SceneKey.Bouncer + } + } /** Notifies that the lock button on the lock screen was clicked. */ fun onLockButtonClicked() { diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index afefccb272146..17470998cf74a 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -178,12 +178,24 @@ constructor( } WakefulnessState.STARTING_TO_WAKE -> { val authMethod = authenticationInteractor.getAuthenticationMethod() - if (authMethod == AuthenticationMethodModel.None) { - switchToScene( - targetSceneKey = SceneKey.Gone, - loggingReason = - "device is starting to wake up while auth method is 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", + ) + } + authMethod.isSecure && isUnlocked -> { + switchToScene( + targetSceneKey = SceneKey.Gone, + loggingReason = + "device is starting to wake up while unlocked with a" + + " secure auth method", + ) + } } } else -> Unit diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index a537121758ea7..8caf6dc3e28aa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -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 - } - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index 45db7a0b17f15..951cadd7664e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -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() = + fun switchFromBouncerToGoneWhenDeviceUnlocked() = 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() = - 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() = + fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn() = 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() = - 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() = + fun switchToLockscreenWhenDeviceSleepsLocked() = 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() = - 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,11 +249,26 @@ class SceneContainerStartableTest : SysuiTestCase() { } @Test - fun switchToGoneWhenDeviceStartsToWakeUp_authMethodNotNone_featureEnabled() = + fun stayOnLockscreenWhenDeviceStartsToWakeUp_authMethodSwipe() = + testScope.runTest { + val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key }) + prepareState( + initialSceneKey = SceneKey.Lockscreen, + authenticationMethod = AuthenticationMethodModel.Swipe, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + underTest.start() + + keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun doesNotSwitchToGoneWhenDeviceStartsToWakeUp_authMethodSecure() = testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key }) prepareState( - isFeatureEnabled = true, initialSceneKey = SceneKey.Lockscreen, authenticationMethod = AuthenticationMethodModel.Pin, ) @@ -373,30 +281,31 @@ class SceneContainerStartableTest : SysuiTestCase() { } @Test - fun switchToGoneWhenDeviceStartsToWakeUp_authMethodNone_featureDisabled() = + fun switchToGoneWhenDeviceStartsToWakeUp_authMethodSecure_deviceUnlocked() = testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key }) prepareState( - isFeatureEnabled = false, initialSceneKey = SceneKey.Lockscreen, - authenticationMethod = AuthenticationMethodModel.None, + 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.Lockscreen) + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) } private fun prepareState( - isFeatureEnabled: Boolean = true, isDeviceUnlocked: Boolean = false, isBypassEnabled: Boolean = false, initialSceneKey: SceneKey? = null, authenticationMethod: AuthenticationMethodModel? = null, ): MutableStateFlow { - 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 ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index 0829f31e3890d..893bbf38ff265 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -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 + } + } } }