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

Commit da5c584f authored by Josh Tsuji's avatar Josh Tsuji
Browse files

Add dismissKeyguardWithCallback to handle KeyguardService#dismiss.

Bug: 278086361
Test: CTS presubmits
Flag: com.android.systemui.keyguard_wm_state_refactor
Change-Id: I87a30ac9aac9209b014919c3a21f964c4110214f
parent 8702521d
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ import com.android.systemui.SystemUIApplication;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Main;
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.KeyguardWakeDirectlyToGoneInteractor;
@@ -319,6 +320,7 @@ public class KeyguardService extends Service {
    private final WindowManagerOcclusionManager mWmOcclusionManager;
    private final KeyguardEnabledInteractor mKeyguardEnabledInteractor;
    private final KeyguardWakeDirectlyToGoneInteractor mKeyguardWakeDirectlyToGoneInteractor;
    private final KeyguardDismissInteractor mKeyguardDismissInteractor;
    private final Lazy<FoldGracePeriodProvider> mFoldGracePeriodProvider = new Lazy<>() {
        @Override
        public FoldGracePeriodProvider get() {
@@ -346,7 +348,8 @@ public class KeyguardService extends Service {
            KeyguardInteractor keyguardInteractor,
            KeyguardEnabledInteractor keyguardEnabledInteractor,
            Lazy<KeyguardStateCallbackStartable> keyguardStateCallbackStartableLazy,
            KeyguardWakeDirectlyToGoneInteractor keyguardWakeDirectlyToGoneInteractor) {
            KeyguardWakeDirectlyToGoneInteractor keyguardWakeDirectlyToGoneInteractor,
            KeyguardDismissInteractor keyguardDismissInteractor) {
        super();
        mKeyguardViewMediator = keyguardViewMediator;
        mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -375,6 +378,7 @@ public class KeyguardService extends Service {
        mWmOcclusionManager = windowManagerOcclusionManager;
        mKeyguardEnabledInteractor = keyguardEnabledInteractor;
        mKeyguardWakeDirectlyToGoneInteractor = keyguardWakeDirectlyToGoneInteractor;
        mKeyguardDismissInteractor = keyguardDismissInteractor;
    }

    @Override
@@ -482,8 +486,12 @@ public class KeyguardService extends Service {
        public void dismiss(IKeyguardDismissCallback callback, CharSequence message) {
            trace("dismiss message=" + message);
            checkPermission();
            if (KeyguardWmStateRefactor.isEnabled()) {
                mKeyguardDismissInteractor.dismissKeyguardWithCallback(callback);
            } else {
                mKeyguardViewMediator.dismiss(callback, message);
            }
        }

        @Override // Binder interface
        public void onDreamingStarted() {
+39 −3
Original line number Diff line number Diff line
@@ -16,9 +16,13 @@

package com.android.systemui.keyguard.domain.interactor

import com.android.internal.policy.IKeyguardDismissCallback
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.keyguard.shared.model.DismissAction
@@ -28,23 +32,30 @@ import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
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.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

/** Encapsulates business logic for requesting the keyguard to dismiss/finish/done. */
@SysUISingleton
class KeyguardDismissInteractor
@Inject
constructor(
    trustRepository: TrustRepository,
    @Main private val mainDispatcher: CoroutineDispatcher,
    @Application private val scope: CoroutineScope,
    private val keyguardRepository: KeyguardRepository,
    primaryBouncerInteractor: PrimaryBouncerInteractor,
    private val primaryBouncerInteractor: PrimaryBouncerInteractor,
    private val selectedUserInteractor: SelectedUserInteractor,
    private val dismissCallbackRegistry: DismissCallbackRegistry,
    trustRepository: TrustRepository,
    alternateBouncerInteractor: AlternateBouncerInteractor,
    powerInteractor: PowerInteractor,
    private val selectedUserInteractor: SelectedUserInteractor,
) {
    /*
     * Updates when a biometric has authenticated the device and is requesting to dismiss
@@ -127,4 +138,29 @@ constructor(
    suspend fun setKeyguardDone(keyguardDoneTiming: KeyguardDone) {
        keyguardRepository.setKeyguardDone(keyguardDoneTiming)
    }

    /**
     * Dismiss the keyguard (or show the bouncer) and invoke the provided callback once dismissed.
     *
     * TODO(b/358412565): Support dismiss messages.
     */
    fun dismissKeyguardWithCallback(
        callback: IKeyguardDismissCallback?,
    ) {
        scope.launch {
            withContext(mainDispatcher) {
                if (callback != null) {
                    dismissCallbackRegistry.addCallback(callback)
                }

                // This will either show the bouncer, or dismiss the keyguard if insecure.
                // We currently need to request showing the primary bouncer in order to start a
                // transition to PRIMARY_BOUNCER. Once we refactor that so that starting the
                // transition is what causes the bouncer to show, we can remove this entire method,
                // and simply ask KeyguardTransitionInteractor to transition to a bouncer state or
                // dismiss keyguard.
                primaryBouncerInteractor.show(true)
            }
        }
    }
}
+6 −15
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
@@ -62,26 +63,19 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() {
    private val keyguardRepository = kosmos.fakeKeyguardRepository
    private val testScope = kosmos.testScope

    private lateinit var dismissInteractorWithDependencies:
        KeyguardDismissInteractorFactory.WithDependencies
    private lateinit var dismissInteractor: KeyguardDismissInteractor
    private lateinit var underTest: KeyguardDismissActionInteractor

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)

        dismissInteractorWithDependencies =
            KeyguardDismissInteractorFactory.create(
                context = context,
                testScope = testScope,
                keyguardRepository = keyguardRepository,
            )

        dismissInteractor = kosmos.keyguardDismissInteractor
        underTest =
            KeyguardDismissActionInteractor(
                repository = keyguardRepository,
                transitionInteractor = kosmos.keyguardTransitionInteractor,
                dismissInteractor = dismissInteractorWithDependencies.interactor,
                dismissInteractor = dismissInteractor,
                applicationScope = testScope.backgroundScope,
                sceneInteractor = kosmos.sceneInteractor,
                deviceEntryInteractor = kosmos.deviceEntryInteractor,
@@ -166,9 +160,7 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() {
                    willAnimateOnLockscreen = true,
                )
            )
            dismissInteractorWithDependencies.bouncerRepository.setKeyguardAuthenticatedBiometrics(
                true
            )
            kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true)
            assertThat(executeDismissAction).isEqualTo(onDismissAction)
        }

@@ -307,8 +299,7 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() {
    @Test
    fun setKeyguardDone() =
        testScope.runTest {
            val keyguardDoneTiming by
                collectLastValue(dismissInteractorWithDependencies.interactor.keyguardDone)
            val keyguardDoneTiming by collectLastValue(dismissInteractor.keyguardDone)
            runCurrent()

            underTest.setKeyguardDone(KeyguardDone.LATER)
+29 −28
Original line number Diff line number Diff line
@@ -23,11 +23,18 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.TrustGrantFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeTrustRepository
import com.android.systemui.keyguard.shared.model.DismissAction
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.keyguard.shared.model.TrustModel
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -38,14 +45,16 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoAnnotations

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardDismissInteractorTest : SysuiTestCase() {
    private val kosmos = testKosmos()

    private lateinit var dispatcher: TestDispatcher
    private lateinit var testScope: TestScope

    private lateinit var underTestDependencies: KeyguardDismissInteractorFactory.WithDependencies
    private lateinit var underTest: KeyguardDismissInteractor
    private val underTest = kosmos.keyguardDismissInteractor
    private val userInfo = UserInfo(0, "", 0)

    @Before
@@ -54,13 +63,7 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
        dispatcher = StandardTestDispatcher()
        testScope = TestScope(dispatcher)

        underTestDependencies =
            KeyguardDismissInteractorFactory.create(
                context = context,
                testScope = testScope,
            )
        underTest = underTestDependencies.interactor
        underTestDependencies.userRepository.setUserInfos(listOf(userInfo))
        kosmos.fakeUserRepository.setUserInfos(listOf(userInfo))
    }

    @Test
@@ -69,10 +72,10 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
            val dismissKeyguardRequestWithoutImmediateDismissAction by
                collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)

            underTestDependencies.bouncerRepository.setKeyguardAuthenticatedBiometrics(null)
            kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null)
            assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()

            underTestDependencies.bouncerRepository.setKeyguardAuthenticatedBiometrics(true)
            kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true)
            assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isEqualTo(Unit)
        }

@@ -81,7 +84,7 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
        testScope.runTest {
            val dismissKeyguardRequestWithoutImmediateDismissAction by
                collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
            underTestDependencies.trustRepository.setRequestDismissKeyguard(
            kosmos.fakeTrustRepository.setRequestDismissKeyguard(
                TrustModel(
                    true,
                    0,
@@ -90,8 +93,8 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
            )
            assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()

            underTestDependencies.powerRepository.setInteractive(true)
            underTestDependencies.trustRepository.setRequestDismissKeyguard(
            kosmos.fakePowerRepository.setInteractive(true)
            kosmos.fakeTrustRepository.setRequestDismissKeyguard(
                TrustModel(
                    true,
                    0,
@@ -106,15 +109,15 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
        testScope.runTest {
            val dismissKeyguardRequestWithoutImmediateDismissAction by
                collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
            underTestDependencies.userRepository.setSelectedUserInfo(userInfo)
            kosmos.fakeUserRepository.setSelectedUserInfo(userInfo)
            runCurrent()

            // authenticated different user
            underTestDependencies.bouncerRepository.setKeyguardAuthenticatedPrimaryAuth(22)
            kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedPrimaryAuth(22)
            assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()

            // authenticated correct user
            underTestDependencies.bouncerRepository.setKeyguardAuthenticatedPrimaryAuth(userInfo.id)
            kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedPrimaryAuth(userInfo.id)
            assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isEqualTo(Unit)
        }

@@ -123,17 +126,15 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
        testScope.runTest {
            val dismissKeyguardRequestWithoutImmediateDismissAction by
                collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
            underTestDependencies.userRepository.setSelectedUserInfo(userInfo)
            kosmos.fakeUserRepository.setSelectedUserInfo(userInfo)
            runCurrent()

            // requested from different user
            underTestDependencies.bouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
                22
            )
            kosmos.fakeKeyguardBouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(22)
            assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()

            // requested from correct user
            underTestDependencies.bouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
            kosmos.fakeKeyguardBouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
                userInfo.id
            )
            assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isEqualTo(Unit)
@@ -159,10 +160,10 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
                collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
            val dismissKeyguardRequestWithImmediateDismissAction by
                collectLastValue(underTest.dismissKeyguardRequestWithImmediateDismissAction)
            underTestDependencies.userRepository.setSelectedUserInfo(userInfo)
            kosmos.fakeUserRepository.setSelectedUserInfo(userInfo)
            runCurrent()

            underTestDependencies.keyguardRepository.setDismissAction(
            kosmos.fakeKeyguardRepository.setDismissAction(
                DismissAction.RunImmediately(
                    onDismissAction = { KeyguardDone.IMMEDIATE },
                    onCancelAction = {},
@@ -170,7 +171,7 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
                    willAnimateOnLockscreen = true,
                )
            )
            underTestDependencies.bouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
            kosmos.fakeKeyguardBouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
                userInfo.id
            )
            assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()
@@ -184,10 +185,10 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
                collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
            val dismissKeyguardRequestWithImmediateDismissAction by
                collectLastValue(underTest.dismissKeyguardRequestWithImmediateDismissAction)
            underTestDependencies.userRepository.setSelectedUserInfo(userInfo)
            kosmos.fakeUserRepository.setSelectedUserInfo(userInfo)
            runCurrent()

            underTestDependencies.keyguardRepository.setDismissAction(
            kosmos.fakeKeyguardRepository.setDismissAction(
                DismissAction.RunAfterKeyguardGone(
                    dismissAction = {},
                    onCancelAction = {},
@@ -195,7 +196,7 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
                    willAnimateOnLockscreen = true,
                )
            )
            underTestDependencies.bouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
            kosmos.fakeKeyguardBouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
                userInfo.id
            )
            assertThat(dismissKeyguardRequestWithImmediateDismissAction).isNull()
+0 −129
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.content.Context
import android.os.Handler
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.time.FakeSystemClock
import kotlinx.coroutines.test.TestScope
import org.mockito.Mockito.mock

/**
 * Helper to create a new KeyguardDismissInteractor in a way that doesn't require modifying many
 * tests whenever we add a constructor param.
 */
object KeyguardDismissInteractorFactory {
    @JvmOverloads
    @JvmStatic
    fun create(
        context: Context,
        testScope: TestScope,
        trustRepository: FakeTrustRepository = FakeTrustRepository(),
        keyguardRepository: FakeKeyguardRepository = FakeKeyguardRepository(),
        bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(),
        keyguardUpdateMonitor: KeyguardUpdateMonitor = mock(KeyguardUpdateMonitor::class.java),
        powerRepository: FakePowerRepository = FakePowerRepository(),
        userRepository: FakeUserRepository = FakeUserRepository(),
    ): WithDependencies {
        val primaryBouncerInteractor =
            PrimaryBouncerInteractor(
                bouncerRepository,
                mock(BouncerView::class.java),
                mock(Handler::class.java),
                mock(KeyguardStateController::class.java),
                mock(KeyguardSecurityModel::class.java),
                mock(PrimaryBouncerCallbackInteractor::class.java),
                mock(FalsingCollector::class.java),
                mock(DismissCallbackRegistry::class.java),
                context,
                keyguardUpdateMonitor,
                trustRepository,
                testScope.backgroundScope,
                mock(SelectedUserInteractor::class.java),
                mock(DeviceEntryFaceAuthInteractor::class.java),
            )
        val alternateBouncerInteractor =
            AlternateBouncerInteractor(
                mock(StatusBarStateController::class.java),
                mock(KeyguardStateController::class.java),
                bouncerRepository,
                FakeFingerprintPropertyRepository(),
                FakeBiometricSettingsRepository(),
                FakeSystemClock(),
                keyguardUpdateMonitor,
                { mock(DeviceEntryBiometricsAllowedInteractor::class.java) },
                { mock(KeyguardInteractor::class.java) },
                { mock(KeyguardTransitionInteractor::class.java) },
                { mock(SceneInteractor::class.java) },
                testScope.backgroundScope,
            )
        val powerInteractorWithDeps =
            PowerInteractorFactory.create(
                repository = powerRepository,
            )
        val selectedUserInteractor = SelectedUserInteractor(repository = userRepository)
        return WithDependencies(
            trustRepository = trustRepository,
            keyguardRepository = keyguardRepository,
            bouncerRepository = bouncerRepository,
            keyguardUpdateMonitor = keyguardUpdateMonitor,
            powerRepository = powerRepository,
            userRepository = userRepository,
            interactor =
                KeyguardDismissInteractor(
                    trustRepository,
                    keyguardRepository,
                    primaryBouncerInteractor,
                    alternateBouncerInteractor,
                    powerInteractorWithDeps.powerInteractor,
                    selectedUserInteractor,
                ),
        )
    }

    data class WithDependencies(
        val trustRepository: FakeTrustRepository,
        val keyguardRepository: FakeKeyguardRepository,
        val bouncerRepository: FakeKeyguardBouncerRepository,
        val keyguardUpdateMonitor: KeyguardUpdateMonitor,
        val powerRepository: FakePowerRepository,
        val userRepository: FakeUserRepository,
        val interactor: KeyguardDismissInteractor,
    )
}
Loading