Loading packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -1285,6 +1285,16 @@ flag { is_fixed_read_only: true } flag { name: "dream_biometric_prompt_fixes" namespace: "systemui" description: "Flags the behavior of turning off dream back gesture disablement and gesture handling when the biometric prompt is showing" bug: "413511537" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "app_clips_backlinks" namespace: "systemui" Loading packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt +277 −125 Original line number Diff line number Diff line Loading @@ -36,12 +36,12 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.test.filters.SmallTest import com.android.app.viewcapture.ViewCaptureFactory import com.android.compose.animation.scene.ObservableTransitionState import com.android.internal.logging.UiEventLogger import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.Flags.FLAG_DREAM_BIOMETRIC_PROMPT_FIXES import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2 import com.android.systemui.Flags.FLAG_SCENE_CONTAINER import com.android.systemui.SysuiTestCase Loading @@ -49,6 +49,8 @@ import com.android.systemui.ambient.touch.TouchHandler import com.android.systemui.ambient.touch.TouchMonitor import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent import com.android.systemui.ambient.touch.scrim.ScrimController import com.android.systemui.biometrics.data.repository.promptRepository import com.android.systemui.biometrics.domain.interactor.promptCredentialInteractor import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository import com.android.systemui.communal.domain.interactor.CommunalInteractor Loading @@ -72,9 +74,8 @@ import com.android.systemui.keyguard.gesture.domain.gestureInteractor import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.navigationbar.gestural.domain.GestureInteractor import com.android.systemui.navigationbar.gestural.data.gestureRepository import com.android.systemui.navigationbar.gestural.domain.TaskInfo import com.android.systemui.navigationbar.gestural.domain.TaskMatcher import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.data.repository.sceneContainerRepository import com.android.systemui.scene.domain.interactor.sceneInteractor Loading Loading @@ -149,8 +150,6 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { private val lifecycleRegistry = FakeLifecycleRegistry(mLifecycleOwner) private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository private val communalRepository = kosmos.fakeCommunalSceneRepository private var viewCaptureSpy = spy(ViewCaptureFactory.getInstance(context)) private val gestureInteractor = spy(kosmos.gestureInteractor) private lateinit var mCommunalInteractor: CommunalInteractor private lateinit var environmentComponents: EnvironmentComponents Loading Loading @@ -245,6 +244,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { // it to testDispatcher. Dispatchers.setMain(kosmos.testDispatcher) onTeardown { Dispatchers.resetMain() } with(kosmos) { mService = DreamOverlayService( mContext, Loading @@ -259,21 +259,23 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { mKeyguardUpdateMonitor, mScrimController, mCommunalInteractor, kosmos.communalSettingsInteractor, kosmos.sceneInteractor, promptCredentialInteractor, communalSettingsInteractor, sceneInteractor, mSystemDialogsCloser, mUiEventLogger, mTouchInsetManager, LOW_LIGHT_COMPONENT, HOME_CONTROL_PANEL_DREAM_COMPONENT, mDreamOverlayCallbackController, kosmos.keyguardInteractor, keyguardInteractor, gestureInteractor, kosmos.wakeGestureMonitor, kosmos.powerInteractor, wakeGestureMonitor, powerInteractor, WINDOW_NAME, ) } } private val client: IDreamOverlayClient get() { Loading Loading @@ -1035,7 +1037,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { bouncerRepository.setPrimaryShow(true) mMainExecutor.runAllReady() // Lifecycle state goes from resumed back to started when the notification shade shows. // Lifecycle state goes from resumed back to started when the bouncer shows. assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.STARTED) // Bouncer closes. Loading Loading @@ -1080,6 +1082,88 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED) } @DisableFlags(FLAG_SCENE_CONTAINER) @Test fun testBouncerShown_stopsGestureBlocking() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Do not show dream complications. client.startDream( mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false, /*isPreview*/ false, /*shouldShowComplication*/ ) mMainExecutor.runAllReady() // GestureBlockedMatcher added when overlay starts. assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) val matcher = gestureRepository.gestureBlockedMatchers.value.first() // Matcher matches dream activity. val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) assertThat(matcher.matches(dreamTaskInfo)).isTrue() // Bouncer shows. bouncerRepository.setPrimaryShow(true) mMainExecutor.runAllReady() // Matcher is removed. assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty() // Bouncer closes. bouncerRepository.setPrimaryShow(false) mMainExecutor.runAllReady() // Matcher is added again. assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) } @EnableFlags(FLAG_SCENE_CONTAINER) @Test fun testBouncerShown_withSceneContainer_stopsGestureBlocking() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Do not show dream complications. client.startDream( mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false, /*isPreview*/ false, /*shouldShowComplication*/ ) mMainExecutor.runAllReady() // GestureBlockedMatcher added when overlay starts. assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) val matcher = gestureRepository.gestureBlockedMatchers.value.first() // Matcher matches dream activity. val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) assertThat(matcher.matches(dreamTaskInfo)).isTrue() // Bouncer shows. kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "test") kosmos.sceneInteractor.showOverlay(Overlays.Bouncer, "test") mMainExecutor.runAllReady() // Matcher is removed. assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty() // Bouncer closes. kosmos.sceneInteractor.changeScene(Scenes.Dream, "test") kosmos.sceneInteractor.hideOverlay(Overlays.Bouncer, "test") mMainExecutor.runAllReady() // Matcher is added again. assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) } @Test @DisableFlags(FLAG_SCENE_CONTAINER) fun testCommunalVisible_setsLifecycleState() = Loading @@ -1104,7 +1188,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal) mMainExecutor.runAllReady() // Lifecycle state goes from resumed back to started when the notification shade shows. // Lifecycle state goes from resumed back to started when communal shows. assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.STARTED) // Communal closes. Loading Loading @@ -1140,7 +1224,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal) mMainExecutor.runAllReady() // Lifecycle state goes from resumed back to started when the notification shade shows. // Lifecycle state goes from resumed back to started when the communal shows. assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.STARTED) // Communal closes. Loading Loading @@ -1184,7 +1268,8 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { } @Test fun testDreamActivityGesturesBlockedWhenDreaming() { fun testDreamActivityGesturesBlockedWhenDreaming() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Loading @@ -1197,23 +1282,22 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { ) mMainExecutor.runAllReady() val matcherCaptor = argumentCaptor<TaskMatcher>() verify(gestureInteractor) .addGestureBlockedMatcher(matcherCaptor.capture(), eq(GestureInteractor.Scope.Global)) val matcher = matcherCaptor.firstValue assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) val matcher = gestureRepository.gestureBlockedMatchers.value.first() val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) assertThat(matcher.matches(dreamTaskInfo)).isTrue() client.endDream() mMainExecutor.runAllReady() verify(gestureInteractor) .removeGestureBlockedMatcher(eq(matcher), eq(GestureInteractor.Scope.Global)) assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty() } @Test fun testDreamActivityGesturesNotBlockedWhenPreview() { fun testDreamActivityGesturesNotBlockedWhenPreview() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Loading @@ -1226,12 +1310,12 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { ) mMainExecutor.runAllReady() verify(gestureInteractor, never()) .addGestureBlockedMatcher(any(), eq(GestureInteractor.Scope.Global)) assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty() } @Test fun testDreamActivityGesturesNotBlockedWhenNotificationShadeShowing() { fun testDreamActivityGesturesNotBlockedWhenNotificationShadeShowing() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Loading @@ -1244,12 +1328,11 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { ) mMainExecutor.runAllReady() val matcherCaptor = argumentCaptor<TaskMatcher>() verify(gestureInteractor) .addGestureBlockedMatcher(matcherCaptor.capture(), eq(GestureInteractor.Scope.Global)) val matcher = matcherCaptor.firstValue assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) val matcher = gestureRepository.gestureBlockedMatchers.value.first() val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) assertThat(matcher.matches(dreamTaskInfo)).isTrue() val callbackCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>() Loading @@ -1259,12 +1342,12 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { callbackCaptor.lastValue.onShadeExpandedChanged(true) mMainExecutor.runAllReady() verify(gestureInteractor) .removeGestureBlockedMatcher(eq(matcher), eq(GestureInteractor.Scope.Global)) assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty() } @Test fun testDreamActivityGesturesNotBlockedDreamEndedBeforeKeyguardStateChanged() { fun testDreamActivityGesturesNotBlockedDreamEndedBeforeKeyguardStateChanged() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Loading @@ -1277,17 +1360,15 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { ) mMainExecutor.runAllReady() val matcherCaptor = argumentCaptor<TaskMatcher>() verify(gestureInteractor) .addGestureBlockedMatcher(matcherCaptor.capture(), eq(GestureInteractor.Scope.Global)) val matcher = matcherCaptor.firstValue assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) val matcher = gestureRepository.gestureBlockedMatchers.value.first() val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) assertThat(matcher.matches(dreamTaskInfo)).isTrue() client.endDream() mMainExecutor.runAllReady() clearInvocations(gestureInteractor) val callbackCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>() verify(mKeyguardUpdateMonitor).registerCallback(callbackCaptor.capture()) Loading @@ -1296,8 +1377,79 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { callbackCaptor.lastValue.onShadeExpandedChanged(true) mMainExecutor.runAllReady() verify(gestureInteractor) .removeGestureBlockedMatcher(eq(matcher), eq(GestureInteractor.Scope.Global)) assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty() } @EnableFlags(FLAG_DREAM_BIOMETRIC_PROMPT_FIXES) @Test fun testBiometricPromptShowing_setsLifecycleState() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Do not show dream complications. client.startDream( mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*isPreview*/, false, /*shouldShowComplication*/ ) mMainExecutor.runAllReady() assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED) // Biometric prompt shows. promptRepository.setIsShowing(true) mMainExecutor.runAllReady() // Lifecycle state goes from resumed back to started when the biometric prompt shows. assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.STARTED) // Biometric prompt closes. promptRepository.setIsShowing(false) mMainExecutor.runAllReady() // Lifecycle state goes back to RESUMED. assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED) } @EnableFlags(FLAG_DREAM_BIOMETRIC_PROMPT_FIXES) @Test fun testBiometricPromptShowing_stopsGestureBlocking() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Do not show dream complications. client.startDream( mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*isPreview*/, false, /*shouldShowComplication*/ ) mMainExecutor.runAllReady() // GestureBlockedMatcher added when overlay starts. assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) val matcher = gestureRepository.gestureBlockedMatchers.value.first() // Matcher matches dream activity. val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) assertThat(matcher.matches(dreamTaskInfo)).isTrue() // Biometric prompt shows. promptRepository.setIsShowing(true) mMainExecutor.runAllReady() // Matcher is removed. assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty() // Biometric prompt closes. promptRepository.setIsShowing(false) mMainExecutor.runAllReady() // Matcher is added again. assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) } @Test Loading packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +36 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.dreams; import static android.service.dreams.Flags.dreamWakeRedirect; import static android.service.dreams.Flags.dreamsV2; import static com.android.systemui.Flags.dreamBiometricPromptFixes; import static com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming; import static com.android.systemui.ambient.touch.TouchSurfaceKt.SURFACE_DREAM; import static com.android.systemui.ambient.touch.scrim.dagger.ScrimModule.BOUNCER_SCRIM_CONTROLLER; Loading Loading @@ -60,6 +61,7 @@ import com.android.systemui.ambient.touch.TouchHandler; import com.android.systemui.ambient.touch.TouchMonitor; import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent; import com.android.systemui.ambient.touch.scrim.ScrimController; import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor; import com.android.systemui.communal.domain.interactor.CommunalInteractor; import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor; import com.android.systemui.communal.shared.log.CommunalUiEvent; Loading Loading @@ -152,6 +154,14 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ */ private boolean mBouncerShowing = false; /** * True if the biometric prompt is showing. * * The biometric prompt is a window that shows up on top of an activity that can be used to * request authentication for a sensitive action. */ private boolean mBiometricPromptShowing = false; private final DreamComplicationComponent.Factory mDreamComplicationComponentFactory; private final ComplicationComponent.Factory mComplicationComponentFactory; private final DreamOverlayComponent.Factory mDreamOverlayComponentFactory; Loading Loading @@ -248,6 +258,13 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ } }; private final Consumer<Boolean> mBiometricPromptShowingConsumer = new Consumer<>() { @Override public void accept(Boolean showing) { mExecutor.execute(() -> updateBiometricPromptShowingLocked(showing)); } }; /** * {@link ResetHandler} protects resetting {@link DreamOverlayService} by making sure reset * requests are processed before subsequent actions proceed. Requests themselves are also Loading Loading @@ -404,6 +421,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ KeyguardUpdateMonitor keyguardUpdateMonitor, @Named(BOUNCER_SCRIM_CONTROLLER) ScrimController bouncerScrimController, CommunalInteractor communalInteractor, PromptCredentialInteractor promptCredentialInteractor, CommunalSettingsInteractor communalSettingsInteractor, SceneInteractor sceneInteractor, SystemDialogsCloser systemDialogsCloser, Loading Loading @@ -466,6 +484,10 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mFlows.add(collectFlow(getLifecycle(), wakeGestureMonitor.getWakeUpDetected(), mPickupConsumer)); } if (dreamBiometricPromptFixes()) { mFlows.add(collectFlow(getLifecycle(), promptCredentialInteractor.isShowing(), mBiometricPromptShowingConsumer)); } } @NonNull Loading Loading @@ -622,7 +644,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ private void updateGestureBlockingLocked() { final boolean shouldBlock = mStarted && !mShadeExpanded && !mBouncerShowing && !isDreamInPreviewMode(); && !isDreamInPreviewMode() && !mBiometricPromptShowing; if (shouldBlock) { mGestureInteractor.addGestureBlockedMatcher(DREAM_TYPE_MATCHER, Loading @@ -648,7 +670,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ } // If anything is on top of the dream, we should stop touch handling. boolean shouldPause = mShadeExpanded || mCommunalVisible || mBouncerShowing; boolean shouldPause = mShadeExpanded || mCommunalVisible || mBouncerShowing || mBiometricPromptShowing; setLifecycleStateLocked( shouldPause ? Lifecycle.State.STARTED : Lifecycle.State.RESUMED); Loading Loading @@ -770,4 +793,15 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ updateLifecycleStateLocked(); updateGestureBlockingLocked(); } private void updateBiometricPromptShowingLocked(boolean biometricPromptShowing) { if (mBiometricPromptShowing == biometricPromptShowing) { return; } mBiometricPromptShowing = biometricPromptShowing; updateLifecycleStateLocked(); updateGestureBlockingLocked(); } } Loading
packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -1285,6 +1285,16 @@ flag { is_fixed_read_only: true } flag { name: "dream_biometric_prompt_fixes" namespace: "systemui" description: "Flags the behavior of turning off dream back gesture disablement and gesture handling when the biometric prompt is showing" bug: "413511537" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "app_clips_backlinks" namespace: "systemui" Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt +277 −125 Original line number Diff line number Diff line Loading @@ -36,12 +36,12 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.test.filters.SmallTest import com.android.app.viewcapture.ViewCaptureFactory import com.android.compose.animation.scene.ObservableTransitionState import com.android.internal.logging.UiEventLogger import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.Flags.FLAG_DREAM_BIOMETRIC_PROMPT_FIXES import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2 import com.android.systemui.Flags.FLAG_SCENE_CONTAINER import com.android.systemui.SysuiTestCase Loading @@ -49,6 +49,8 @@ import com.android.systemui.ambient.touch.TouchHandler import com.android.systemui.ambient.touch.TouchMonitor import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent import com.android.systemui.ambient.touch.scrim.ScrimController import com.android.systemui.biometrics.data.repository.promptRepository import com.android.systemui.biometrics.domain.interactor.promptCredentialInteractor import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository import com.android.systemui.communal.domain.interactor.CommunalInteractor Loading @@ -72,9 +74,8 @@ import com.android.systemui.keyguard.gesture.domain.gestureInteractor import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.navigationbar.gestural.domain.GestureInteractor import com.android.systemui.navigationbar.gestural.data.gestureRepository import com.android.systemui.navigationbar.gestural.domain.TaskInfo import com.android.systemui.navigationbar.gestural.domain.TaskMatcher import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.data.repository.sceneContainerRepository import com.android.systemui.scene.domain.interactor.sceneInteractor Loading Loading @@ -149,8 +150,6 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { private val lifecycleRegistry = FakeLifecycleRegistry(mLifecycleOwner) private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository private val communalRepository = kosmos.fakeCommunalSceneRepository private var viewCaptureSpy = spy(ViewCaptureFactory.getInstance(context)) private val gestureInteractor = spy(kosmos.gestureInteractor) private lateinit var mCommunalInteractor: CommunalInteractor private lateinit var environmentComponents: EnvironmentComponents Loading Loading @@ -245,6 +244,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { // it to testDispatcher. Dispatchers.setMain(kosmos.testDispatcher) onTeardown { Dispatchers.resetMain() } with(kosmos) { mService = DreamOverlayService( mContext, Loading @@ -259,21 +259,23 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { mKeyguardUpdateMonitor, mScrimController, mCommunalInteractor, kosmos.communalSettingsInteractor, kosmos.sceneInteractor, promptCredentialInteractor, communalSettingsInteractor, sceneInteractor, mSystemDialogsCloser, mUiEventLogger, mTouchInsetManager, LOW_LIGHT_COMPONENT, HOME_CONTROL_PANEL_DREAM_COMPONENT, mDreamOverlayCallbackController, kosmos.keyguardInteractor, keyguardInteractor, gestureInteractor, kosmos.wakeGestureMonitor, kosmos.powerInteractor, wakeGestureMonitor, powerInteractor, WINDOW_NAME, ) } } private val client: IDreamOverlayClient get() { Loading Loading @@ -1035,7 +1037,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { bouncerRepository.setPrimaryShow(true) mMainExecutor.runAllReady() // Lifecycle state goes from resumed back to started when the notification shade shows. // Lifecycle state goes from resumed back to started when the bouncer shows. assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.STARTED) // Bouncer closes. Loading Loading @@ -1080,6 +1082,88 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED) } @DisableFlags(FLAG_SCENE_CONTAINER) @Test fun testBouncerShown_stopsGestureBlocking() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Do not show dream complications. client.startDream( mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false, /*isPreview*/ false, /*shouldShowComplication*/ ) mMainExecutor.runAllReady() // GestureBlockedMatcher added when overlay starts. assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) val matcher = gestureRepository.gestureBlockedMatchers.value.first() // Matcher matches dream activity. val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) assertThat(matcher.matches(dreamTaskInfo)).isTrue() // Bouncer shows. bouncerRepository.setPrimaryShow(true) mMainExecutor.runAllReady() // Matcher is removed. assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty() // Bouncer closes. bouncerRepository.setPrimaryShow(false) mMainExecutor.runAllReady() // Matcher is added again. assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) } @EnableFlags(FLAG_SCENE_CONTAINER) @Test fun testBouncerShown_withSceneContainer_stopsGestureBlocking() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Do not show dream complications. client.startDream( mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false, /*isPreview*/ false, /*shouldShowComplication*/ ) mMainExecutor.runAllReady() // GestureBlockedMatcher added when overlay starts. assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) val matcher = gestureRepository.gestureBlockedMatchers.value.first() // Matcher matches dream activity. val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) assertThat(matcher.matches(dreamTaskInfo)).isTrue() // Bouncer shows. kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "test") kosmos.sceneInteractor.showOverlay(Overlays.Bouncer, "test") mMainExecutor.runAllReady() // Matcher is removed. assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty() // Bouncer closes. kosmos.sceneInteractor.changeScene(Scenes.Dream, "test") kosmos.sceneInteractor.hideOverlay(Overlays.Bouncer, "test") mMainExecutor.runAllReady() // Matcher is added again. assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) } @Test @DisableFlags(FLAG_SCENE_CONTAINER) fun testCommunalVisible_setsLifecycleState() = Loading @@ -1104,7 +1188,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal) mMainExecutor.runAllReady() // Lifecycle state goes from resumed back to started when the notification shade shows. // Lifecycle state goes from resumed back to started when communal shows. assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.STARTED) // Communal closes. Loading Loading @@ -1140,7 +1224,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal) mMainExecutor.runAllReady() // Lifecycle state goes from resumed back to started when the notification shade shows. // Lifecycle state goes from resumed back to started when the communal shows. assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.STARTED) // Communal closes. Loading Loading @@ -1184,7 +1268,8 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { } @Test fun testDreamActivityGesturesBlockedWhenDreaming() { fun testDreamActivityGesturesBlockedWhenDreaming() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Loading @@ -1197,23 +1282,22 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { ) mMainExecutor.runAllReady() val matcherCaptor = argumentCaptor<TaskMatcher>() verify(gestureInteractor) .addGestureBlockedMatcher(matcherCaptor.capture(), eq(GestureInteractor.Scope.Global)) val matcher = matcherCaptor.firstValue assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) val matcher = gestureRepository.gestureBlockedMatchers.value.first() val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) assertThat(matcher.matches(dreamTaskInfo)).isTrue() client.endDream() mMainExecutor.runAllReady() verify(gestureInteractor) .removeGestureBlockedMatcher(eq(matcher), eq(GestureInteractor.Scope.Global)) assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty() } @Test fun testDreamActivityGesturesNotBlockedWhenPreview() { fun testDreamActivityGesturesNotBlockedWhenPreview() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Loading @@ -1226,12 +1310,12 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { ) mMainExecutor.runAllReady() verify(gestureInteractor, never()) .addGestureBlockedMatcher(any(), eq(GestureInteractor.Scope.Global)) assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty() } @Test fun testDreamActivityGesturesNotBlockedWhenNotificationShadeShowing() { fun testDreamActivityGesturesNotBlockedWhenNotificationShadeShowing() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Loading @@ -1244,12 +1328,11 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { ) mMainExecutor.runAllReady() val matcherCaptor = argumentCaptor<TaskMatcher>() verify(gestureInteractor) .addGestureBlockedMatcher(matcherCaptor.capture(), eq(GestureInteractor.Scope.Global)) val matcher = matcherCaptor.firstValue assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) val matcher = gestureRepository.gestureBlockedMatchers.value.first() val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) assertThat(matcher.matches(dreamTaskInfo)).isTrue() val callbackCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>() Loading @@ -1259,12 +1342,12 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { callbackCaptor.lastValue.onShadeExpandedChanged(true) mMainExecutor.runAllReady() verify(gestureInteractor) .removeGestureBlockedMatcher(eq(matcher), eq(GestureInteractor.Scope.Global)) assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty() } @Test fun testDreamActivityGesturesNotBlockedDreamEndedBeforeKeyguardStateChanged() { fun testDreamActivityGesturesNotBlockedDreamEndedBeforeKeyguardStateChanged() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Loading @@ -1277,17 +1360,15 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { ) mMainExecutor.runAllReady() val matcherCaptor = argumentCaptor<TaskMatcher>() verify(gestureInteractor) .addGestureBlockedMatcher(matcherCaptor.capture(), eq(GestureInteractor.Scope.Global)) val matcher = matcherCaptor.firstValue assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) val matcher = gestureRepository.gestureBlockedMatchers.value.first() val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) assertThat(matcher.matches(dreamTaskInfo)).isTrue() client.endDream() mMainExecutor.runAllReady() clearInvocations(gestureInteractor) val callbackCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>() verify(mKeyguardUpdateMonitor).registerCallback(callbackCaptor.capture()) Loading @@ -1296,8 +1377,79 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { callbackCaptor.lastValue.onShadeExpandedChanged(true) mMainExecutor.runAllReady() verify(gestureInteractor) .removeGestureBlockedMatcher(eq(matcher), eq(GestureInteractor.Scope.Global)) assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty() } @EnableFlags(FLAG_DREAM_BIOMETRIC_PROMPT_FIXES) @Test fun testBiometricPromptShowing_setsLifecycleState() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Do not show dream complications. client.startDream( mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*isPreview*/, false, /*shouldShowComplication*/ ) mMainExecutor.runAllReady() assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED) // Biometric prompt shows. promptRepository.setIsShowing(true) mMainExecutor.runAllReady() // Lifecycle state goes from resumed back to started when the biometric prompt shows. assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.STARTED) // Biometric prompt closes. promptRepository.setIsShowing(false) mMainExecutor.runAllReady() // Lifecycle state goes back to RESUMED. assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED) } @EnableFlags(FLAG_DREAM_BIOMETRIC_PROMPT_FIXES) @Test fun testBiometricPromptShowing_stopsGestureBlocking() = kosmos.runTest { val client = client // Inform the overlay service of dream starting. Do not show dream complications. client.startDream( mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*isPreview*/, false, /*shouldShowComplication*/ ) mMainExecutor.runAllReady() // GestureBlockedMatcher added when overlay starts. assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) val matcher = gestureRepository.gestureBlockedMatchers.value.first() // Matcher matches dream activity. val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) assertThat(matcher.matches(dreamTaskInfo)).isTrue() // Biometric prompt shows. promptRepository.setIsShowing(true) mMainExecutor.runAllReady() // Matcher is removed. assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty() // Biometric prompt closes. promptRepository.setIsShowing(false) mMainExecutor.runAllReady() // Matcher is added again. assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1) } @Test Loading
packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +36 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.dreams; import static android.service.dreams.Flags.dreamWakeRedirect; import static android.service.dreams.Flags.dreamsV2; import static com.android.systemui.Flags.dreamBiometricPromptFixes; import static com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming; import static com.android.systemui.ambient.touch.TouchSurfaceKt.SURFACE_DREAM; import static com.android.systemui.ambient.touch.scrim.dagger.ScrimModule.BOUNCER_SCRIM_CONTROLLER; Loading Loading @@ -60,6 +61,7 @@ import com.android.systemui.ambient.touch.TouchHandler; import com.android.systemui.ambient.touch.TouchMonitor; import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent; import com.android.systemui.ambient.touch.scrim.ScrimController; import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor; import com.android.systemui.communal.domain.interactor.CommunalInteractor; import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor; import com.android.systemui.communal.shared.log.CommunalUiEvent; Loading Loading @@ -152,6 +154,14 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ */ private boolean mBouncerShowing = false; /** * True if the biometric prompt is showing. * * The biometric prompt is a window that shows up on top of an activity that can be used to * request authentication for a sensitive action. */ private boolean mBiometricPromptShowing = false; private final DreamComplicationComponent.Factory mDreamComplicationComponentFactory; private final ComplicationComponent.Factory mComplicationComponentFactory; private final DreamOverlayComponent.Factory mDreamOverlayComponentFactory; Loading Loading @@ -248,6 +258,13 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ } }; private final Consumer<Boolean> mBiometricPromptShowingConsumer = new Consumer<>() { @Override public void accept(Boolean showing) { mExecutor.execute(() -> updateBiometricPromptShowingLocked(showing)); } }; /** * {@link ResetHandler} protects resetting {@link DreamOverlayService} by making sure reset * requests are processed before subsequent actions proceed. Requests themselves are also Loading Loading @@ -404,6 +421,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ KeyguardUpdateMonitor keyguardUpdateMonitor, @Named(BOUNCER_SCRIM_CONTROLLER) ScrimController bouncerScrimController, CommunalInteractor communalInteractor, PromptCredentialInteractor promptCredentialInteractor, CommunalSettingsInteractor communalSettingsInteractor, SceneInteractor sceneInteractor, SystemDialogsCloser systemDialogsCloser, Loading Loading @@ -466,6 +484,10 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mFlows.add(collectFlow(getLifecycle(), wakeGestureMonitor.getWakeUpDetected(), mPickupConsumer)); } if (dreamBiometricPromptFixes()) { mFlows.add(collectFlow(getLifecycle(), promptCredentialInteractor.isShowing(), mBiometricPromptShowingConsumer)); } } @NonNull Loading Loading @@ -622,7 +644,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ private void updateGestureBlockingLocked() { final boolean shouldBlock = mStarted && !mShadeExpanded && !mBouncerShowing && !isDreamInPreviewMode(); && !isDreamInPreviewMode() && !mBiometricPromptShowing; if (shouldBlock) { mGestureInteractor.addGestureBlockedMatcher(DREAM_TYPE_MATCHER, Loading @@ -648,7 +670,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ } // If anything is on top of the dream, we should stop touch handling. boolean shouldPause = mShadeExpanded || mCommunalVisible || mBouncerShowing; boolean shouldPause = mShadeExpanded || mCommunalVisible || mBouncerShowing || mBiometricPromptShowing; setLifecycleStateLocked( shouldPause ? Lifecycle.State.STARTED : Lifecycle.State.RESUMED); Loading Loading @@ -770,4 +793,15 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ updateLifecycleStateLocked(); updateGestureBlockingLocked(); } private void updateBiometricPromptShowingLocked(boolean biometricPromptShowing) { if (mBiometricPromptShowing == biometricPromptShowing) { return; } mBiometricPromptShowing = biometricPromptShowing; updateLifecycleStateLocked(); updateGestureBlockingLocked(); } }