diff options
| author | 2024-10-26 03:50:30 +0000 | |
|---|---|---|
| committer | 2024-10-26 03:50:30 +0000 | |
| commit | 0b814eb27025ce8d1a0026723c60b47a8a7c3fc1 (patch) | |
| tree | d0e73a1c99e44ecd7e8ed0391523853b9b077ffc | |
| parent | 82571cdd8afaf7804ed389a6c09dd7c6b4327c17 (diff) | |
| parent | 285e3a396daa3521725150a3ee89d658e0240559 (diff) | |
Merge "Add KeyguardLockWhileAwakeInteractor." into main
8 files changed, 283 insertions, 37 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt new file mode 100644 index 000000000000..bd26e4205242 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectValues +import com.android.systemui.keyguard.data.repository.biometricSettingsRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.coroutines.test.advanceTimeBy +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +@kotlinx.coroutines.ExperimentalCoroutinesApi +class KeyguardLockWhileAwakeInteractorTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private lateinit var underTest: KeyguardLockWhileAwakeInteractor + + @Before + fun setup() { + underTest = kosmos.keyguardLockWhileAwakeInteractor + } + + @Test + fun emitsMultipleTimeoutEvents() = + testScope.runTest { + val values by collectValues(underTest.lockWhileAwakeEvents) + + underTest.onKeyguardServiceDoKeyguardTimeout(options = null) + runCurrent() + + assertThat(values) + .containsExactly(LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON) + + advanceTimeBy(1000) + underTest.onKeyguardServiceDoKeyguardTimeout(options = null) + runCurrent() + + assertThat(values) + .containsExactly( + LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON, + LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON, + ) + } + + @Test + fun emitsWhenKeyguardEnabled_onlyIfShowingWhenDisabled() = + testScope.runTest { + val values by collectValues(underTest.lockWhileAwakeEvents) + + kosmos.biometricSettingsRepository.setIsUserInLockdown(false) + runCurrent() + + kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false) + runCurrent() + + assertEquals(0, values.size) + + kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true) + runCurrent() + + assertThat(values).containsExactly(LockWhileAwakeReason.KEYGUARD_REENABLED) + + kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + testScope = testScope, + ) + kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false) + kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true) + runCurrent() + + assertThat(values).containsExactly(LockWhileAwakeReason.KEYGUARD_REENABLED) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index f4d2ea018a44..c8a16483a00c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -560,7 +560,9 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest .startedTransition( to = KeyguardState.LOCKSCREEN, from = KeyguardState.GONE, - ownerName = "FromGoneTransitionInteractor", + ownerName = + "FromGoneTransitionInteractor" + + "(keyguard interactor says keyguard is showing)", animatorAssertion = { it.isNotNull() }, ) @@ -640,7 +642,9 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest .startedTransition( to = KeyguardState.GLANCEABLE_HUB, from = KeyguardState.GONE, - ownerName = FromGoneTransitionInteractor::class.simpleName, + ownerName = + FromGoneTransitionInteractor::class.simpleName + + "(keyguard interactor says keyguard is showing)", animatorAssertion = { it.isNotNull() }, ) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 67625d04b467..32c2bc7c8620 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -81,6 +81,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardLockWhileAwakeInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardStateCallbackInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardWakeDirectlyToGoneInteractor; import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier; @@ -329,6 +330,8 @@ public class KeyguardService extends Service { return new FoldGracePeriodProvider(); } }; + private final KeyguardLockWhileAwakeInteractor + mKeyguardLockWhileAwakeInteractor; @Inject public KeyguardService( @@ -353,7 +356,8 @@ public class KeyguardService extends Service { KeyguardWakeDirectlyToGoneInteractor keyguardWakeDirectlyToGoneInteractor, KeyguardDismissInteractor keyguardDismissInteractor, Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy, - KeyguardStateCallbackInteractor keyguardStateCallbackInteractor) { + KeyguardStateCallbackInteractor keyguardStateCallbackInteractor, + KeyguardLockWhileAwakeInteractor keyguardLockWhileAwakeInteractor) { super(); mKeyguardViewMediator = keyguardViewMediator; mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher; @@ -385,6 +389,7 @@ public class KeyguardService extends Service { mKeyguardEnabledInteractor = keyguardEnabledInteractor; mKeyguardWakeDirectlyToGoneInteractor = keyguardWakeDirectlyToGoneInteractor; mKeyguardDismissInteractor = keyguardDismissInteractor; + mKeyguardLockWhileAwakeInteractor = keyguardLockWhileAwakeInteractor; } @Override @@ -656,6 +661,11 @@ public class KeyguardService extends Service { public void doKeyguardTimeout(Bundle options) { trace("doKeyguardTimeout"); checkPermission(); + + if (KeyguardWmStateRefactor.isEnabled()) { + mKeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout(options); + } + mKeyguardViewMediator.doKeyguardTimeout(options); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt index bc2ed71f156c..bd9836f3489d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt @@ -24,8 +24,6 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.KeyguardWmStateRefactor -import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository -import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled @@ -54,9 +52,7 @@ constructor( powerInteractor: PowerInteractor, private val communalSceneInteractor: CommunalSceneInteractor, keyguardOcclusionInteractor: KeyguardOcclusionInteractor, - private val biometricSettingsRepository: BiometricSettingsRepository, - private val keyguardRepository: KeyguardRepository, - private val keyguardEnabledInteractor: KeyguardEnabledInteractor, + private val keyguardLockWhileAwakeInteractor: KeyguardLockWhileAwakeInteractor, ) : TransitionInteractor( fromState = KeyguardState.GONE, @@ -78,7 +74,9 @@ constructor( } fun showKeyguard() { - scope.launch("$TAG#showKeyguard") { startTransitionTo(KeyguardState.LOCKSCREEN) } + scope.launch("$TAG#showKeyguard") { + startTransitionTo(KeyguardState.LOCKSCREEN, ownerReason = "showKeyguard()") + } } /** @@ -102,34 +100,18 @@ constructor( // Primarily for when the user chooses to lock down the device private fun listenForGoneToLockscreenOrHubOrOccluded() { if (KeyguardWmStateRefactor.isEnabled) { - scope.launch("$TAG#listenForGoneToLockscreenOrHub") { - biometricSettingsRepository.isCurrentUserInLockdown - .distinctUntilChanged() - .filterRelevantKeyguardStateAnd { inLockdown -> inLockdown } + scope.launch { + keyguardLockWhileAwakeInteractor.lockWhileAwakeEvents + .filterRelevantKeyguardState() .sample(communalSceneInteractor.isIdleOnCommunalNotEditMode, ::Pair) - .collect { (_, isIdleOnCommunal) -> + .collect { (lockReason, idleOnCommunal) -> val to = - if (isIdleOnCommunal) { + if (idleOnCommunal) { KeyguardState.GLANCEABLE_HUB } else { KeyguardState.LOCKSCREEN } - startTransitionTo(to, ownerReason = "User initiated lockdown") - } - } - - scope.launch { - keyguardRepository.isKeyguardEnabled - .filterRelevantKeyguardStateAnd { enabled -> enabled } - .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled) - .filter { reshow -> reshow } - .collect { - startTransitionTo( - KeyguardState.LOCKSCREEN, - ownerReason = - "Keyguard was re-enabled, and we weren't GONE when it " + - "was originally disabled", - ) + startTransitionTo(to, ownerReason = "lockWhileAwake: $lockReason") } } } else { @@ -146,7 +128,10 @@ constructor( } else { KeyguardState.LOCKSCREEN } - startTransitionTo(to) + startTransitionTo( + to, + ownerReason = "keyguard interactor says keyguard is showing", + ) } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index 8b75545fddc9..b60e98a68162 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -288,6 +288,7 @@ constructor( startTransitionTo( KeyguardState.GONE, modeOnCanceled = TransitionModeOnCanceled.RESET, + ownerReason = "keyguard interactor says keyguard is going away", ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt new file mode 100644 index 000000000000..0ab3e5c0927f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.os.Bundle +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository +import com.android.systemui.util.kotlin.sample +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge + +/** + * Emitted when we receive a [KeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout] + * call. + * + * Includes a timestamp so it's not conflated by the StateFlow. + */ +data class KeyguardTimeoutWhileAwakeEvent(val timestamp: Long, val options: Bundle?) + +/** The reason we're locking while awake, used for logging. */ +enum class LockWhileAwakeReason(private val logReason: String) { + LOCKDOWN("Lockdown initiated."), + KEYGUARD_REENABLED( + "Keyguard was re-enabled. We weren't unlocked when it was disabled, " + + "so we're returning to the lockscreen." + ), + KEYGUARD_TIMEOUT_WHILE_SCREEN_ON( + "Timed out while the screen was kept on, or WM#lockNow() was called." + ); + + override fun toString(): String { + return logReason + } +} + +/** + * Logic around cases where the device locks while still awake (transitioning from GONE -> + * LOCKSCREEN), vs. the more common cases of a power button press or screen timeout, which result in + * the device going to sleep. + * + * This is possible in the following situations: + * - The user initiates lockdown from the power menu. + * - Theft detection, etc. has requested lockdown. + * - The keyguard was disabled while visible, and has now been re-enabled, so it's re-showing. + * - Someone called WM#lockNow(). + * - The screen timed out, but an activity with FLAG_ALLOW_LOCK_WHILE_SCREEN_ON is on top. + */ +@SysUISingleton +class KeyguardLockWhileAwakeInteractor +@Inject +constructor( + biometricSettingsRepository: BiometricSettingsRepository, + keyguardEnabledInteractor: KeyguardEnabledInteractor, +) { + /** Emits whenever a timeout event is received by [KeyguardService]. */ + private val timeoutEvents: MutableStateFlow<KeyguardTimeoutWhileAwakeEvent?> = + MutableStateFlow(null) + + /** Emits whenever the current user is in lockdown mode. */ + private val inLockdown: Flow<LockWhileAwakeReason> = + biometricSettingsRepository.isCurrentUserInLockdown + .distinctUntilChanged() + .filter { inLockdown -> inLockdown } + .map { LockWhileAwakeReason.LOCKDOWN } + + /** + * Emits whenever the keyguard is re-enabled, and we need to return to lockscreen due to the + * device being locked when the keyguard was originally disabled. + */ + private val keyguardReenabled: Flow<LockWhileAwakeReason> = + keyguardEnabledInteractor.isKeyguardEnabled + .filter { enabled -> enabled } + .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled) + .filter { reshow -> reshow } + .map { LockWhileAwakeReason.KEYGUARD_REENABLED } + + /** Emits whenever we should lock while the screen is on, for any reason. */ + val lockWhileAwakeEvents: Flow<LockWhileAwakeReason> = + merge( + inLockdown, + keyguardReenabled, + timeoutEvents.filterNotNull().map { + LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON + }, + ) + + /** + * Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that + * the device locked while the screen was on. + * + * [options] appears to be no longer used, but we'll keep it in this interactor in case that + * turns out not to be true. + */ + fun onKeyguardServiceDoKeyguardTimeout(options: Bundle?) { + timeoutEvents.value = + KeyguardTimeoutWhileAwakeEvent( + timestamp = System.currentTimeMillis(), + options = options, + ) + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt index 700d7e9c5608..ff0f13e7111f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt @@ -17,8 +17,6 @@ package com.android.systemui.keyguard.domain.interactor import com.android.systemui.communal.domain.interactor.communalSceneInteractor -import com.android.systemui.keyguard.data.repository.biometricSettingsRepository -import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope @@ -39,8 +37,6 @@ val Kosmos.fromGoneTransitionInteractor by powerInteractor = powerInteractor, communalSceneInteractor = communalSceneInteractor, keyguardOcclusionInteractor = keyguardOcclusionInteractor, - biometricSettingsRepository = biometricSettingsRepository, - keyguardRepository = keyguardRepository, - keyguardEnabledInteractor = keyguardEnabledInteractor, + keyguardLockWhileAwakeInteractor = keyguardLockWhileAwakeInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt new file mode 100644 index 000000000000..39236c7c9fae --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import com.android.systemui.keyguard.data.repository.biometricSettingsRepository +import com.android.systemui.kosmos.Kosmos + +val Kosmos.keyguardLockWhileAwakeInteractor by + Kosmos.Fixture { + KeyguardLockWhileAwakeInteractor( + biometricSettingsRepository = biometricSettingsRepository, + keyguardEnabledInteractor = keyguardEnabledInteractor, + ) + } |