diff options
7 files changed, 170 insertions, 72 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt index 030233625027..39baa01e07d6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt @@ -570,7 +570,7 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() { unlockDevice() assertThat(isUnlocked).isTrue() - underTest.lockNow() + underTest.lockNow("test") runCurrent() assertThat(isUnlocked).isFalse() @@ -597,6 +597,62 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() { assertThat(deviceUnlockStatus?.isUnlocked).isFalse() } + @Test + fun deviceUnlockStatus_staysUnlocked_whenDeviceGoesToSleep_whileIsTrusted() = + testScope.runTest { + setLockAfterScreenTimeout(5000) + kosmos.fakeAuthenticationRepository.powerButtonInstantlyLocks = false + val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus) + + kosmos.fakeTrustRepository.setCurrentUserTrusted(true) + + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + runCurrent() + assertThat(deviceUnlockStatus?.isUnlocked).isTrue() + + kosmos.powerInteractor.setAsleepForTest( + sleepReason = PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON + ) + runCurrent() + + assertThat(deviceUnlockStatus?.isUnlocked).isTrue() + } + + @Test + fun deviceUnlockStatus_staysUnlocked_whileIsTrusted() = + testScope.runTest { + val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus) + kosmos.fakeTrustRepository.setCurrentUserTrusted(true) + unlockDevice() + + kosmos.powerInteractor.setAsleepForTest( + sleepReason = PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON + ) + runCurrent() + + assertThat(deviceUnlockStatus?.isUnlocked).isTrue() + } + + @Test + fun deviceUnlockStatus_becomesLocked_whenNoLongerTrusted_whileAsleep() = + testScope.runTest { + val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus) + kosmos.fakeTrustRepository.setCurrentUserTrusted(true) + unlockDevice() + kosmos.powerInteractor.setAsleepForTest( + sleepReason = PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON + ) + runCurrent() + assertThat(deviceUnlockStatus?.isUnlocked).isTrue() + + kosmos.fakeTrustRepository.setCurrentUserTrusted(false) + runCurrent() + + assertThat(deviceUnlockStatus?.isUnlocked).isFalse() + } + private fun TestScope.unlockDevice() { val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index af30e435da73..5648c62fb439 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -2697,6 +2697,34 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(isVisible).isFalse() } + @Test + fun deviceLocks_whenNoLongerTrusted_whileDeviceNotEntered() = + testScope.runTest { + prepareState(isDeviceUnlocked = true, initialSceneKey = Scenes.Gone) + underTest.start() + + val isDeviceEntered by collectLastValue(kosmos.deviceEntryInteractor.isDeviceEntered) + val deviceUnlockStatus by + collectLastValue(kosmos.deviceUnlockedInteractor.deviceUnlockStatus) + val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) + assertThat(isDeviceEntered).isTrue() + assertThat(deviceUnlockStatus?.isUnlocked).isTrue() + assertThat(currentScene).isEqualTo(Scenes.Gone) + kosmos.fakeTrustRepository.setCurrentUserTrusted(true) + kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "reason") + runCurrent() + assertThat(isDeviceEntered).isFalse() + assertThat(deviceUnlockStatus?.isUnlocked).isTrue() + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + + kosmos.fakeTrustRepository.setCurrentUserTrusted(false) + runCurrent() + + assertThat(isDeviceEntered).isFalse() + assertThat(deviceUnlockStatus?.isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + } + private fun TestScope.emulateSceneTransition( transitionStateFlow: MutableStateFlow<ObservableTransitionState>, toScene: SceneKey, diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index 5b6859761705..5fc924b14814 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -16,7 +16,6 @@ package com.android.systemui.deviceentry.domain.interactor -import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.policy.IKeyguardDismissCallback import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.authentication.shared.model.AuthenticationMethodModel @@ -36,11 +35,9 @@ import com.android.systemui.util.kotlin.pairwise import com.android.systemui.utils.coroutines.flow.mapLatestConflated import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter @@ -285,32 +282,7 @@ constructor( } /** Locks the device instantly. */ - fun lockNow() { - deviceUnlockedInteractor.lockNow() - } - - suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) { - coroutineScope { - launch { - isDeviceEntered - .logDiffsForTable( - tableLogBuffer = tableLogBuffer, - columnName = "isDeviceEntered", - initialValue = isDeviceEntered.value, - ) - .collect() - } - - launch { - canSwipeToEnter - .map { it?.toString() ?: "" } - .logDiffsForTable( - tableLogBuffer = tableLogBuffer, - columnName = "canSwipeToEnter", - initialValue = canSwipeToEnter.value?.toString() ?: "", - ) - .collect() - } - } + fun lockNow(debuggingReason: String) { + deviceUnlockedInteractor.lockNow(debuggingReason) } } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt index b1be9a209a0a..c6ae35317c72 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt @@ -59,6 +59,7 @@ import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.receiveAsFlow @@ -70,7 +71,7 @@ class DeviceUnlockedInteractor constructor( private val authenticationInteractor: AuthenticationInteractor, private val repository: DeviceEntryRepository, - trustInteractor: TrustInteractor, + private val trustInteractor: TrustInteractor, faceAuthInteractor: DeviceEntryFaceAuthInteractor, fingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor, private val powerInteractor: PowerInteractor, @@ -181,7 +182,8 @@ constructor( val deviceUnlockStatus: StateFlow<DeviceUnlockStatus> = repository.deviceUnlockStatus.asStateFlow() - private val lockNowRequests = Channel<Unit>() + /** A [Channel] of "lock now" requests where the values are the debugging reasons. */ + private val lockNowRequests = Channel<String>() override suspend fun onActivated(): Nothing { coroutineScope { @@ -218,8 +220,8 @@ constructor( } /** Locks the device instantly. */ - fun lockNow() { - lockNowRequests.trySend(Unit) + fun lockNow(debuggingReason: String) { + lockNowRequests.trySend(debuggingReason) } private suspend fun handleLockAndUnlockEvents() { @@ -244,47 +246,70 @@ constructor( private suspend fun handleLockEvents() { merge( - // Device wakefulness events. - powerInteractor.detailedWakefulness - .map { Pair(it.isAsleep(), it.lastSleepReason) } - .distinctUntilChangedBy { it.first } - .map { (isAsleep, lastSleepReason) -> - if (isAsleep) { - if ( - (lastSleepReason == WakeSleepReason.POWER_BUTTON) && - authenticationInteractor.getPowerButtonInstantlyLocks() - ) { - LockImmediately("locked instantly from power button") - } else if (lastSleepReason == WakeSleepReason.SLEEP_BUTTON) { - LockImmediately("locked instantly from sleep button") - } else { - LockWithDelay("entering sleep") - } - } else { - CancelDelayedLock("waking up") - } - }, + trustInteractor.isTrusted.flatMapLatestConflated { isTrusted -> + if (isTrusted) { + // When entering a trusted environment, power-related lock events are + // ignored. + Log.d(TAG, "In trusted environment, ignoring power-related lock events") + flowOf(CancelDelayedLock("in trusted environment")) + } else { + // When not in a trusted environment, power-related lock events are treated + // as normal. + Log.d( + TAG, + "Not in trusted environment, power-related lock events treated as" + + " normal", + ) + merge( + // Device wakefulness events. + powerInteractor.detailedWakefulness + .map { Pair(it.isAsleep(), it.lastSleepReason) } + .distinctUntilChangedBy { it.first } + .map { (isAsleep, lastSleepReason) -> + if (isAsleep) { + if ( + (lastSleepReason == WakeSleepReason.POWER_BUTTON) && + authenticationInteractor + .getPowerButtonInstantlyLocks() + ) { + LockImmediately("locked instantly from power button") + } else if ( + lastSleepReason == WakeSleepReason.SLEEP_BUTTON + ) { + LockImmediately("locked instantly from sleep button") + } else { + LockWithDelay("entering sleep") + } + } else { + CancelDelayedLock("waking up") + } + }, + // 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() + } + }, + ) + } + }, // Device enters lockdown. isInLockdown .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() - } - }, - lockNowRequests.receiveAsFlow().map { LockImmediately("lockNow") }, + lockNowRequests.receiveAsFlow().map { reason -> LockImmediately(reason) }, ) .collectLatest(::onLockEvent) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 1d36076347bf..c1a59f180225 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -671,7 +671,7 @@ public class KeyguardService extends Service { checkPermission(); if (SceneContainerFlag.isEnabled()) { - mDeviceEntryInteractorLazy.get().lockNow(); + mDeviceEntryInteractorLazy.get().lockNow("doKeyguardTimeout"); } else if (KeyguardWmStateRefactor.isEnabled()) { mKeyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(options); } 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 2fd584176220..94e32fcb9ac6 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 @@ -44,6 +44,7 @@ import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.TrustInteractor import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor.Companion.keyguardScenes import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.model.SceneContainerPlugin @@ -148,6 +149,7 @@ constructor( private val activityTransitionAnimator: ActivityTransitionAnimator, private val shadeModeInteractor: ShadeModeInteractor, @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer, + private val trustInteractor: TrustInteractor, ) : CoreStartable { private val centralSurfaces: CentralSurfaces? get() = centralSurfacesOptLazy.get().getOrNull() @@ -173,6 +175,7 @@ constructor( notifyKeyguardDismissCancelledCallbacks() refreshLockscreenEnabled() hydrateActivityTransitionAnimationState() + lockWhenDeviceBecomesUntrusted() } else { sceneLogger.logFrameworkEnabled( isEnabled = false, @@ -998,6 +1001,18 @@ constructor( ) } + private fun lockWhenDeviceBecomesUntrusted() { + applicationScope.launch { + trustInteractor.isTrusted.pairwise().collect { (wasTrusted, isTrusted) -> + if (wasTrusted && !isTrusted && !deviceEntryInteractor.isDeviceEntered.value) { + deviceEntryInteractor.lockNow( + "Exited trusted environment while not device not entered" + ) + } + } + } + } + companion object { private const val TAG = "SceneContainerStartable" } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt index 65bfafbfa9b0..7a9b052481cb 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt @@ -33,6 +33,7 @@ import com.android.systemui.haptics.vibratorHelper import com.android.systemui.keyguard.dismissCallbackRegistry import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor 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 import com.android.systemui.kosmos.testScope @@ -91,5 +92,6 @@ val Kosmos.sceneContainerStartable by Fixture { activityTransitionAnimator = activityTransitionAnimator, shadeModeInteractor = shadeModeInteractor, tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"), + trustInteractor = trustInteractor, ) } |