summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Josh Tsuji <tsuji@google.com> 2024-11-22 21:07:57 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-11-22 21:07:57 +0000
commit43b90eb68c0db7b94fa26430af5d96152ff857b9 (patch)
tree73a0a5839a1cfb348f0dc8fe0044e47361a3321d
parent735f04b65b523ae47e66d760eba8a9745f410eb0 (diff)
parente5e3b5bcae68ef0ec8c0285f3d416827cfd8e313 (diff)
Merge "Add keyguard suppression interactors." into main
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt68
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt125
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt61
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt2
12 files changed, 354 insertions, 135 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
index bd26e4205242..bef995fa5225 100644
--- 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
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.widget.lockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
@@ -33,6 +34,8 @@ import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -52,14 +55,21 @@ class KeyguardLockWhileAwakeInteractorTest : SysuiTestCase() {
testScope.runTest {
val values by collectValues(underTest.lockWhileAwakeEvents)
- underTest.onKeyguardServiceDoKeyguardTimeout(options = null)
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+ runCurrent()
+
+ kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(
+ options = null
+ )
runCurrent()
assertThat(values)
.containsExactly(LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON)
advanceTimeBy(1000)
- underTest.onKeyguardServiceDoKeyguardTimeout(options = null)
+ kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(
+ options = null
+ )
runCurrent()
assertThat(values)
@@ -69,8 +79,15 @@ class KeyguardLockWhileAwakeInteractorTest : SysuiTestCase() {
)
}
+ /**
+ * We re-show keyguard when it's re-enabled, but only if it was originally showing when we
+ * disabled it.
+ *
+ * If it wasn't showing when originally disabled it, re-enabling it should do nothing (the
+ * keyguard will re-show next time we're locked).
+ */
@Test
- fun emitsWhenKeyguardEnabled_onlyIfShowingWhenDisabled() =
+ fun emitsWhenKeyguardReenabled_onlyIfShowingWhenDisabled() =
testScope.runTest {
val values by collectValues(underTest.lockWhileAwakeEvents)
@@ -98,4 +115,49 @@ class KeyguardLockWhileAwakeInteractorTest : SysuiTestCase() {
assertThat(values).containsExactly(LockWhileAwakeReason.KEYGUARD_REENABLED)
}
+
+ /**
+ * Un-suppressing keyguard should never cause us to re-show. We'll re-show when we're next
+ * locked, even if we were showing when originally suppressed.
+ */
+ @Test
+ fun doesNotEmit_keyguardNoLongerSuppressed() =
+ testScope.runTest {
+ val values by collectValues(underTest.lockWhileAwakeEvents)
+
+ // Enable keyguard and then suppress it.
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+ whenever(kosmos.lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true)
+ runCurrent()
+
+ assertEquals(0, values.size)
+
+ // Un-suppress keyguard.
+ whenever(kosmos.lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false)
+ runCurrent()
+
+ assertEquals(0, values.size)
+ }
+
+ /**
+ * Lockdown and lockNow() should not cause us to lock while awake if we are suppressed via adb.
+ */
+ @Test
+ fun doesNotEmit_fromLockdown_orFromLockNow_ifEnabledButSuppressed() =
+ testScope.runTest {
+ val values by collectValues(underTest.lockWhileAwakeEvents)
+
+ // Set keyguard enabled, but then disable lockscreen (suppress it).
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+ whenever(kosmos.lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true)
+ runCurrent()
+
+ kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(null)
+ runCurrent()
+
+ kosmos.biometricSettingsRepository.setIsUserInLockdown(true)
+ runCurrent()
+
+ assertEquals(0, values.size)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
index 7e249e8c179d..ead151ee6df2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
@@ -87,9 +87,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
assertEquals(
listOf(
- false, // Defaults to false.
+ false // Defaults to false.
),
- canWake
+ canWake,
)
repository.setKeyguardEnabled(false)
@@ -100,33 +100,26 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
false, // Default to false.
true, // True now that keyguard service is disabled
),
- canWake
+ canWake,
)
repository.setKeyguardEnabled(true)
runCurrent()
- assertEquals(
- listOf(
- false,
- true,
- false,
- ),
- canWake
- )
+ assertEquals(listOf(false, true, false), canWake)
}
@Test
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
- fun testCanWakeDirectlyToGone_lockscreenDisabledThenEnabled() =
+ fun testCanWakeDirectlyToGone_lockscreenDisabledThenEnabled_onlyAfterWakefulnessChange() =
testScope.runTest {
val canWake by collectValues(underTest.canWakeDirectlyToGone)
assertEquals(
listOf(
- false, // Defaults to false.
+ false // Defaults to false.
),
- canWake
+ canWake,
)
whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true)
@@ -136,9 +129,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
listOf(
// Still false - isLockScreenDisabled only causes canWakeDirectlyToGone to
// update on the next wake/sleep event.
- false,
+ false
),
- canWake
+ canWake,
)
kosmos.powerInteractor.setAsleepForTest()
@@ -150,7 +143,7 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
// True since we slept after setting isLockScreenDisabled=true
true,
),
- canWake
+ canWake,
)
kosmos.powerInteractor.setAwakeForTest()
@@ -159,25 +152,75 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
kosmos.powerInteractor.setAsleepForTest()
runCurrent()
+ assertEquals(listOf(false, true), canWake)
+
+ whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false)
+ kosmos.powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ assertEquals(listOf(false, true, false), canWake)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testCanWakeDirectlyToGone_lockscreenDisabledThenEnabled_lockNowEvent() =
+ testScope.runTest {
+ val canWake by collectValues(underTest.canWakeDirectlyToGone)
+
+ assertEquals(
+ listOf(
+ false // Defaults to false.
+ ),
+ canWake,
+ )
+
+ whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ // Still false - isLockScreenDisabled only causes canWakeDirectlyToGone to
+ // update on the next wakefulness or lockNow event.
+ false
+ ),
+ canWake,
+ )
+
+ kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(null)
+ runCurrent()
+
assertEquals(
listOf(
false,
+ // True when lockNow() called after setting isLockScreenDisabled=true
true,
),
- canWake
+ canWake,
)
whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false)
- kosmos.powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ // Still true since no lockNow() calls made.
+ true,
+ ),
+ canWake,
+ )
+
+ kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(null)
runCurrent()
assertEquals(
listOf(
false,
true,
+ // False again after the lockNow() call.
false,
),
- canWake
+ canWake,
)
}
@@ -189,9 +232,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
assertEquals(
listOf(
- false, // Defaults to false.
+ false // Defaults to false.
),
- canWake
+ canWake,
)
repository.setBiometricUnlockState(BiometricUnlockMode.WAKE_AND_UNLOCK)
@@ -213,9 +256,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
assertEquals(
listOf(
- false, // Defaults to false.
+ false // Defaults to false.
),
- canWake
+ canWake,
)
repository.setCanIgnoreAuthAndReturnToGone(true)
@@ -237,9 +280,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
assertEquals(
listOf(
- false, // Defaults to false.
+ false // Defaults to false.
),
- canWake
+ canWake,
)
whenever(kosmos.devicePolicyManager.getMaximumTimeToLock(eq(null), anyInt()))
@@ -257,13 +300,7 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
)
runCurrent()
- assertEquals(
- listOf(
- false,
- true,
- ),
- canWake
- )
+ assertEquals(listOf(false, true), canWake)
verify(kosmos.alarmManager)
.setExactAndAllowWhileIdle(
@@ -281,9 +318,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
assertEquals(
listOf(
- false, // Defaults to false.
+ false // Defaults to false.
),
- canWake
+ canWake,
)
whenever(kosmos.devicePolicyManager.getMaximumTimeToLock(eq(null), anyInt()))
@@ -312,7 +349,7 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
// Timed out, so we can ignore auth/return to GONE.
true,
),
- canWake
+ canWake,
)
verify(kosmos.alarmManager)
@@ -338,7 +375,7 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
// alarm in flight that should be canceled.
false,
),
- canWake
+ canWake,
)
kosmos.powerInteractor.setAsleepForTest(
@@ -354,25 +391,17 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
// Back to sleep.
true,
),
- canWake
+ canWake,
)
// Simulate the first sleep's alarm coming in.
lastRegisteredBroadcastReceiver?.onReceive(
kosmos.mockedContext,
- Intent("com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD")
+ Intent("com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD"),
)
runCurrent()
// It should not have any effect.
- assertEquals(
- listOf(
- false,
- true,
- false,
- true,
- ),
- canWake
- )
+ assertEquals(listOf(false, true, false, true), canWake)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 7097c1d2fc4e..d40fe468b0a5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -81,7 +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.KeyguardServiceLockNowInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardStateCallbackInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardWakeDirectlyToGoneInteractor;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier;
@@ -330,8 +330,7 @@ public class KeyguardService extends Service {
return new FoldGracePeriodProvider();
}
};
- private final KeyguardLockWhileAwakeInteractor
- mKeyguardLockWhileAwakeInteractor;
+ private final KeyguardServiceLockNowInteractor mKeyguardServiceLockNowInteractor;
@Inject
public KeyguardService(
@@ -357,7 +356,7 @@ public class KeyguardService extends Service {
KeyguardDismissInteractor keyguardDismissInteractor,
Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy,
KeyguardStateCallbackInteractor keyguardStateCallbackInteractor,
- KeyguardLockWhileAwakeInteractor keyguardLockWhileAwakeInteractor) {
+ KeyguardServiceLockNowInteractor keyguardServiceLockNowInteractor) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -389,7 +388,7 @@ public class KeyguardService extends Service {
mKeyguardEnabledInteractor = keyguardEnabledInteractor;
mKeyguardWakeDirectlyToGoneInteractor = keyguardWakeDirectlyToGoneInteractor;
mKeyguardDismissInteractor = keyguardDismissInteractor;
- mKeyguardLockWhileAwakeInteractor = keyguardLockWhileAwakeInteractor;
+ mKeyguardServiceLockNowInteractor = keyguardServiceLockNowInteractor;
}
@Override
@@ -665,7 +664,7 @@ public class KeyguardService extends Service {
if (SceneContainerFlag.isEnabled()) {
mDeviceEntryInteractorLazy.get().lockNow();
} else if (KeyguardWmStateRefactor.isEnabled()) {
- mKeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout(options);
+ mKeyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(options);
}
mKeyguardViewMediator.doKeyguardTimeout(options);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 01ec4d026646..9f131607cb99 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -3943,7 +3943,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
private void notifyDefaultDisplayCallbacks(boolean showing) {
- if (SceneContainerFlag.isEnabled()) {
+ if (SceneContainerFlag.isEnabled() || KeyguardWmStateRefactor.isEnabled()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
index 631e44aca26d..42cbd7d39248 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -16,39 +16,52 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.internal.widget.LockPatternUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.withContext
/**
- * Logic around the keyguard being enabled/disabled, per [KeyguardService]. If the keyguard is not
- * enabled, the lockscreen cannot be shown and the device will go from AOD/DOZING directly to GONE.
+ * Logic around the keyguard being enabled, disabled, or suppressed via adb. If the keyguard is
+ * disabled or suppressed, the lockscreen cannot be shown and the device will go from AOD/DOZING
+ * directly to GONE.
*
* Keyguard can be disabled by selecting Security: "None" in settings, or by apps that hold
* permission to do so (such as Phone). Some CTS tests also disable keyguard in onCreate or onStart
* rather than simply dismissing the keyguard or setting up the device to have Security: None, for
* reasons unknown.
+ *
+ * Keyguard can be suppressed by calling "adb shell locksettings set-disabled true", which is
+ * frequently done in tests. If keyguard is suppressed, it won't show even if the keyguard is
+ * enabled. If keyguard is not suppressed, then we defer to whether keyguard is enabled or disabled.
*/
@SysUISingleton
class KeyguardEnabledInteractor
@Inject
constructor(
- @Application scope: CoroutineScope,
+ @Application val scope: CoroutineScope,
+ @Background val backgroundDispatcher: CoroutineDispatcher,
val repository: KeyguardRepository,
val biometricSettingsRepository: BiometricSettingsRepository,
- keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val lockPatternUtils: LockPatternUtils,
+ keyguardDismissTransitionInteractor: dagger.Lazy<KeyguardDismissTransitionInteractor>,
internalTransitionInteractor: InternalKeyguardTransitionInteractor,
) {
@@ -62,6 +75,10 @@ constructor(
* If the keyguard is disabled while we're locked, we will transition to GONE unless we're in
* lockdown mode. If the keyguard is re-enabled, we'll transition back to LOCKSCREEN if we were
* locked when it was disabled.
+ *
+ * Even if the keyguard is enabled, it's possible for it to be suppressed temporarily via adb.
+ * If you need to respect that adb command, you will need to use
+ * [isKeyguardEnabledAndNotSuppressed] instead of using this flow.
*/
val isKeyguardEnabled: StateFlow<Boolean> = repository.isKeyguardEnabled
@@ -96,9 +113,9 @@ constructor(
val currentTransitionInfo =
internalTransitionInteractor.currentTransitionInfoInternal()
if (currentTransitionInfo.to != KeyguardState.GONE && !inLockdown) {
- keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
- "keyguard disabled"
- )
+ keyguardDismissTransitionInteractor
+ .get()
+ .startDismissKeyguardTransition("keyguard disabled")
}
}
}
@@ -116,4 +133,37 @@ constructor(
fun isShowKeyguardWhenReenabled(): Boolean {
return repository.isShowKeyguardWhenReenabled()
}
+
+ /**
+ * Whether the keyguard is enabled, and has not been suppressed via adb.
+ *
+ * There is unfortunately no callback for [isKeyguardSuppressed], which means this can't be a
+ * flow, since it's ambiguous when we would query the latest suppression value.
+ */
+ suspend fun isKeyguardEnabledAndNotSuppressed(): Boolean {
+ return isKeyguardEnabled.value && !isKeyguardSuppressed()
+ }
+
+ /**
+ * Returns whether the lockscreen has been disabled ("suppressed") via "adb shell locksettings
+ * set-disabled". If suppressed, we'll ignore all signals that would typically result in showing
+ * the keyguard, regardless of the value of [isKeyguardEnabled].
+ *
+ * It's extremely confusing to have [isKeyguardEnabled] not be the inverse of "is lockscreen
+ * disabled", so this method intentionally re-terms it as "suppressed".
+ *
+ * Note that if the lockscreen is currently showing when it's suppressed, it will remain visible
+ * until it's unlocked, at which point it will never re-appear until suppression is removed.
+ */
+ suspend fun isKeyguardSuppressed(
+ userId: Int = selectedUserInteractor.getSelectedUserId()
+ ): Boolean {
+ // isLockScreenDisabled returns true whenever keyguard is not enabled, even if the adb
+ // command was not used to disable/suppress the lockscreen. To make these booleans as clear
+ // as possible, only return true if keyguard is suppressed when it otherwise would have
+ // been enabled.
+ return withContext(backgroundDispatcher) {
+ isKeyguardEnabled.value && lockPatternUtils.isLockScreenDisabled(userId)
+ }
+ }
}
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
index 0ab3e5c0927f..ce84e71a3562 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt
@@ -16,27 +16,16 @@
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."),
@@ -71,10 +60,8 @@ class KeyguardLockWhileAwakeInteractor
constructor(
biometricSettingsRepository: BiometricSettingsRepository,
keyguardEnabledInteractor: KeyguardEnabledInteractor,
+ keyguardServiceLockNowInteractor: KeyguardServiceLockNowInteractor,
) {
- /** 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> =
@@ -97,25 +84,19 @@ constructor(
/** 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
- },
+ // We're in lockdown, and the keyguard is enabled. If the keyguard is disabled, the
+ // lockdown button is hidden in the UI, but it's still possible to trigger lockdown in
+ // tests.
+ inLockdown
+ .filter { keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed() }
+ .map { LockWhileAwakeReason.LOCKDOWN },
+ // The keyguard was re-enabled, and it was showing when it was originally disabled.
+ // Tests currently expect that if the keyguard is re-enabled, it will show even if it's
+ // suppressed, so we don't check for isKeyguardEnabledAndNotSuppressed() on this flow.
+ keyguardReenabled.map { LockWhileAwakeReason.KEYGUARD_REENABLED },
+ // KeyguardService says we need to lock now, and the lockscreen is enabled.
+ keyguardServiceLockNowInteractor.lockNowEvents
+ .filter { keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed() }
+ .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/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt
new file mode 100644
index 000000000000..9ed53ea74316
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.annotation.SuppressLint
+import android.os.Bundle
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.launch
+
+/**
+ * Emitted when we receive a [KeyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout]
+ * call.
+ */
+data class KeyguardLockNowEvent(val options: Bundle?)
+
+/**
+ * Logic around requests by [KeyguardService] to lock the device right now, even though the device
+ * is awake and not going to sleep.
+ *
+ * This can happen if WM#lockNow() is called, or if the screen is forced to stay awake but the lock
+ * timeout elapses.
+ *
+ * This is not the only way for the device to lock while the screen is on. The other cases, which do
+ * not directly involve [KeyguardService], are handled in [KeyguardLockWhileAwakeInteractor].
+ */
+@SysUISingleton
+class KeyguardServiceLockNowInteractor
+@Inject
+constructor(@Background val backgroundScope: CoroutineScope) {
+
+ /**
+ * Emits whenever [KeyguardService] receives a call that indicates we should lock the device
+ * right now, even though the device is awake and not going to sleep.
+ *
+ * WARNING: This is only one of multiple reasons the device might need to lock while not going
+ * to sleep. Unless you're dealing with keyguard internals that specifically need to know that
+ * we're locking due to a call to doKeyguardTimeout, use
+ * [KeyguardLockWhileAwakeInteractor.lockWhileAwakeEvents].
+ *
+ * This is fundamentally an event flow, hence the SharedFlow.
+ */
+ @SuppressLint("SharedFlowCreation")
+ val lockNowEvents: MutableSharedFlow<KeyguardLockNowEvent> = MutableSharedFlow()
+
+ /**
+ * 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?) {
+ backgroundScope.launch { lockNowEvents.emit(KeyguardLockNowEvent(options = options)) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
index fbc7e2aecbdf..8641dfa5a183 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
@@ -25,6 +25,7 @@ import android.content.Intent
import android.content.IntentFilter
import android.provider.Settings
import android.provider.Settings.Secure
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -48,20 +49,21 @@ import javax.inject.Inject
import kotlin.math.max
import kotlin.math.min
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
/**
* Logic related to the ability to wake directly to GONE from asleep (AOD/DOZING), without going
* through LOCKSCREEN or a BOUNCER state.
*
* This is possible in the following scenarios:
- * - The lockscreen is disabled, either from an app request (SUW does this), or by the security
+ * - The keyguard is not enabled, either from an app request (SUW does this), or by the security
* "None" setting.
+ * - The keyguard was suppressed via adb.
* - A biometric authentication event occurred while we were asleep (fingerprint auth, etc). This
* specifically is referred to throughout the codebase as "wake and unlock".
* - The screen timed out, but the "lock after screen timeout" duration has not elapsed.
@@ -86,43 +88,44 @@ constructor(
private val lockPatternUtils: LockPatternUtils,
private val systemSettings: SystemSettings,
private val selectedUserInteractor: SelectedUserInteractor,
+ keyguardEnabledInteractor: KeyguardEnabledInteractor,
+ keyguardServiceLockNowInteractor: KeyguardServiceLockNowInteractor,
) {
/**
- * Whether the lockscreen was disabled as of the last wake/sleep event, according to
- * LockPatternUtils.
- *
- * This will always be true if [repository.isKeyguardServiceEnabled]=false, but it can also be
- * true when the keyguard service is enabled if the lockscreen has been disabled via adb using
- * the `adb shell locksettings set-disabled true` command, which is often done in tests.
- *
- * Unlike keyguardServiceEnabled, changes to this value should *not* immediately show or hide
- * the keyguard. If the lockscreen is disabled in this way, it will just not show on the next
- * sleep/wake.
+ * Whether the keyguard was suppressed as of the most recent wakefulness event or lockNow
+ * command. Keyguard suppression can only be queried (there is no callback available), and
+ * legacy code only queried the value in onStartedGoingToSleep and doKeyguardTimeout. Tests now
+ * depend on that behavior, so for now, we'll replicate it here.
*/
- private val isLockscreenDisabled: Flow<Boolean> =
- powerInteractor.isAwake.map { isLockscreenDisabled() }
+ private val shouldSuppressKeyguard =
+ merge(powerInteractor.isAwake, keyguardServiceLockNowInteractor.lockNowEvents)
+ .map { keyguardEnabledInteractor.isKeyguardSuppressed() }
+ // Default to false, so that flows that combine this one emit prior to the first
+ // wakefulness emission.
+ .onStart { emit(false) }
/**
* Whether we can wake from AOD/DOZING directly to GONE, bypassing LOCKSCREEN/BOUNCER states.
*
* This is possible in the following cases:
* - Keyguard is disabled, either from an app request or from security being set to "None".
+ * - Keyguard is suppressed, via adb locksettings.
* - We're wake and unlocking (fingerprint auth occurred while asleep).
* - We're allowed to ignore auth and return to GONE, due to timeouts not elapsing.
*/
val canWakeDirectlyToGone =
combine(
repository.isKeyguardEnabled,
- isLockscreenDisabled,
+ shouldSuppressKeyguard,
repository.biometricUnlockState,
repository.canIgnoreAuthAndReturnToGone,
) {
keyguardEnabled,
- isLockscreenDisabled,
+ shouldSuppressKeyguard,
biometricUnlockState,
canIgnoreAuthAndReturnToGone ->
- (!keyguardEnabled || isLockscreenDisabled) ||
+ (!keyguardEnabled || shouldSuppressKeyguard) ||
BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode) ||
canIgnoreAuthAndReturnToGone
}
@@ -186,9 +189,9 @@ constructor(
.sample(
transitionInteractor.isCurrentlyIn(
Scenes.Gone,
- stateWithoutSceneContainer = KeyguardState.GONE
+ stateWithoutSceneContainer = KeyguardState.GONE,
),
- ::Pair
+ ::Pair,
)
.collect { (wakefulness, finishedInGone) ->
// Save isAwake for use in onDreamingStarted/onDreamingStopped.
@@ -260,7 +263,7 @@ constructor(
delayedActionFilter,
SYSTEMUI_PERMISSION,
null /* scheduler */,
- Context.RECEIVER_EXPORTED_UNAUDITED
+ Context.RECEIVER_EXPORTED_UNAUDITED,
)
}
@@ -282,7 +285,7 @@ constructor(
context,
0,
intent,
- PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE,
)
val time = systemClock.elapsedRealtime() + getCanIgnoreAuthAndReturnToGoneDuration()
@@ -311,16 +314,6 @@ constructor(
}
/**
- * Returns whether the lockscreen is disabled, either because the keyguard service is disabled
- * or because an adb command has disabled the lockscreen.
- */
- private fun isLockscreenDisabled(
- userId: Int = selectedUserInteractor.getSelectedUserId()
- ): Boolean {
- return lockPatternUtils.isLockScreenDisabled(userId)
- }
-
- /**
* Returns the duration within which we can return to GONE without auth after a screen timeout
* (or power button press, if lock instantly is disabled).
*
@@ -336,7 +329,7 @@ constructor(
.getIntForUser(
Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
KEYGUARD_CAN_IGNORE_AUTH_DURATION,
- userId
+ userId,
)
.toLong()
@@ -352,7 +345,7 @@ constructor(
.getIntForUser(
Settings.System.SCREEN_OFF_TIMEOUT,
KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT,
- userId
+ userId,
)
.toLong()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
index 007d2297e387..f88ed07046ab 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
@@ -16,18 +16,24 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.internal.widget.lockPatternUtils
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
val Kosmos.keyguardEnabledInteractor by
Kosmos.Fixture {
KeyguardEnabledInteractor(
applicationCoroutineScope,
+ testDispatcher,
keyguardRepository,
biometricSettingsRepository,
- keyguardDismissTransitionInteractor,
+ selectedUserInteractor,
+ lockPatternUtils,
+ { keyguardDismissTransitionInteractor },
internalTransitionInteractor = internalKeyguardTransitionInteractor,
)
}
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
index 39236c7c9fae..2423949cdacc 100644
--- 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
@@ -24,5 +24,6 @@ val Kosmos.keyguardLockWhileAwakeInteractor by
KeyguardLockWhileAwakeInteractor(
biometricSettingsRepository = biometricSettingsRepository,
keyguardEnabledInteractor = keyguardEnabledInteractor,
+ keyguardServiceLockNowInteractor = keyguardServiceLockNowInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt
new file mode 100644
index 000000000000..29335c590a75
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+
+val Kosmos.keyguardServiceLockNowInteractor by
+ Kosmos.Fixture { KeyguardServiceLockNowInteractor(backgroundScope = testScope) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
index 63e168d018bf..4aa132c1d3af 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
@@ -41,5 +41,7 @@ val Kosmos.keyguardWakeDirectlyToGoneInteractor by
lockPatternUtils,
fakeSettings,
selectedUserInteractor,
+ keyguardEnabledInteractor,
+ keyguardServiceLockNowInteractor,
)
}