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

Commit 43b90eb6 authored by Josh Tsuji's avatar Josh Tsuji Committed by Android (Google) Code Review
Browse files

Merge "Add keyguard suppression interactors." into main

parents 735f04b6 e5e3b5bc
Loading
Loading
Loading
Loading
+65 −3
Original line number Diff line number Diff line
@@ -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)
        }
}
+77 −48
Original line number Diff line number Diff line
@@ -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)
        }
}
+5 −6
Original line number Diff line number Diff line
@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -3943,7 +3943,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
    }

    private void notifyDefaultDisplayCallbacks(boolean showing) {
        if (SceneContainerFlag.isEnabled()) {
        if (SceneContainerFlag.isEnabled() || KeyguardWmStateRefactor.isEnabled()) {
            return;
        }

+58 −8
Original line number Diff line number Diff line
@@ -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)
        }
    }
}
Loading