Merge changes from topic "power_button_gh" into main

* changes:
  Return to dream on power button press when occluded by glanceable hub.
  Track dream focus in order to know when dreams are occluded.
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
index 9a02b74b..5da0cb4 100644
--- a/core/java/android/service/dreams/DreamOverlayService.java
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -79,6 +79,11 @@
             mService.endDream(this);
         }
 
+        @Override
+        public void comeToFront() {
+            mService.comeToFront(this);
+        }
+
         private void onExitRequested() {
             try {
                 mDreamOverlayCallback.onExitRequested();
@@ -130,6 +135,16 @@
         });
     }
 
+    private void comeToFront(OverlayClient client) {
+        mExecutor.execute(() -> {
+            if (mCurrentClient != client) {
+                return;
+            }
+
+            onComeToFront();
+        });
+    }
+
     private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() {
         @Override
         public void getClient(IDreamOverlayClientCallback callback) {
@@ -190,6 +205,13 @@
     public void onWakeUp() {}
 
     /**
+     * This method is overridden by implementations to handle when the dream is coming to the front
+     * (after having lost focus to something on top of it).
+     * @hide
+     */
+    public void onComeToFront() {}
+
+    /**
      * This method is overridden by implementations to handle when the dream has ended. There may
      * be earlier signals leading up to this step, such as @{@link #onWakeUp(Runnable)}.
      *
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index f896447..c26d83c 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.service.dreams.Flags.dreamHandlesConfirmKeys;
+import static android.service.dreams.Flags.dreamTracksFocus;
 
 import android.annotation.FlaggedApi;
 import android.annotation.IdRes;
@@ -457,6 +458,15 @@
     /** {@inheritDoc} */
     @Override
     public void onWindowFocusChanged(boolean hasFocus) {
+        if (!dreamTracksFocus()) {
+            return;
+        }
+
+        try {
+            mDreamManager.onDreamFocusChanged(hasFocus);
+        } catch (RemoteException ex) {
+            // system server died
+        }
     }
 
     /** {@inheritDoc} */
@@ -1152,6 +1162,19 @@
         wakeUp(false);
     }
 
+    /**
+     * Tells the dream to come to the front (which in turn tells the overlay to come to the front).
+     */
+    private void comeToFront() {
+        mOverlayConnection.addConsumer(overlay -> {
+            try {
+                overlay.comeToFront();
+            } catch (RemoteException e) {
+                Log.e(mTag, "could not tell overlay to come to front:" + e);
+            }
+        });
+    }
+
     private void wakeUp(boolean fromSystem) {
         if (mDebug) {
             Slog.v(mTag, "wakeUp(): fromSystem=" + fromSystem + ", mWaking=" + mWaking
@@ -1608,6 +1631,15 @@
         public void wakeUp() {
             mHandler.post(() -> DreamService.this.wakeUp(true /*fromSystem*/));
         }
+
+        @Override
+        public void comeToFront() {
+            if (!dreamTracksFocus()) {
+                return;
+            }
+
+            mHandler.post(DreamService.this::comeToFront);
+        }
     }
 
     /** @hide */
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index e45384f..85f0368 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -48,4 +48,5 @@
     void setSystemDreamComponent(in ComponentName componentName);
     void registerDreamOverlayService(in ComponentName componentName);
     void startDreamActivity(in Intent intent);
+    void onDreamFocusChanged(in boolean hasFocus);
 }
diff --git a/core/java/android/service/dreams/IDreamOverlayClient.aidl b/core/java/android/service/dreams/IDreamOverlayClient.aidl
index 78b7280..5054d4d 100644
--- a/core/java/android/service/dreams/IDreamOverlayClient.aidl
+++ b/core/java/android/service/dreams/IDreamOverlayClient.aidl
@@ -42,4 +42,7 @@
 
     /** Called when the dream has ended. */
     void endDream();
+
+    /** Called when the dream is coming to the front. */
+    void comeToFront();
 }
diff --git a/core/java/android/service/dreams/IDreamService.aidl b/core/java/android/service/dreams/IDreamService.aidl
index 8b5d875..2e2651b 100644
--- a/core/java/android/service/dreams/IDreamService.aidl
+++ b/core/java/android/service/dreams/IDreamService.aidl
@@ -25,4 +25,5 @@
     void attach(IBinder windowToken, boolean canDoze, boolean isPreviewMode, IRemoteCallback started);
     void detach();
     void wakeUp();
+    void comeToFront();
 }
diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig
index 88f1090..0a458bc 100644
--- a/core/java/android/service/dreams/flags.aconfig
+++ b/core/java/android/service/dreams/flags.aconfig
@@ -19,3 +19,10 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "dream_tracks_focus"
+  namespace: "communal"
+  description: "This flag enables the ability for dreams to track whether or not they have focus"
+  bug: "331798001"
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index 76f15d2..b4b812d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -32,7 +32,9 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.notificationShadeWindowController
 import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
@@ -68,8 +70,10 @@
                         keyguardTransitionInteractor = keyguardTransitionInteractor,
                         keyguardInteractor = keyguardInteractor,
                         systemSettings = fakeSettings,
+                        notificationShadeWindowController = notificationShadeWindowController,
                         applicationScope = applicationCoroutineScope,
                         bgScope = applicationCoroutineScope,
+                        mainDispatcher = testDispatcher,
                     )
                     .apply { start() }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 41bc1dc..e2e5169 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.dreams;
 
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
@@ -28,6 +30,7 @@
 import android.content.res.Resources;
 import android.graphics.Region;
 import android.os.Handler;
+import android.testing.TestableLooper.RunWithLooper;
 import android.view.AttachedSurfaceControl;
 import android.view.ViewGroup;
 import android.view.ViewRootImpl;
@@ -43,6 +46,7 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
 import com.android.systemui.complication.ComplicationHostViewController;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.statusbar.BlurUtils;
 
 import org.junit.Before;
@@ -52,8 +56,11 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import kotlinx.coroutines.CoroutineDispatcher;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
+@RunWithLooper(setAsMainLooper = true)
 public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
     private static final int MAX_BURN_IN_OFFSET = 20;
     private static final long BURN_IN_PROTECTION_UPDATE_INTERVAL = 10;
@@ -87,6 +94,9 @@
     Handler mHandler;
 
     @Mock
+    CoroutineDispatcher mDispatcher;
+
+    @Mock
     BlurUtils mBlurUtils;
 
     @Mock
@@ -103,6 +113,8 @@
 
     @Mock
     DreamOverlayStateController mStateController;
+    @Mock
+    KeyguardTransitionInteractor mKeyguardTransitionInteractor;
 
     DreamOverlayContainerViewController mController;
 
@@ -115,6 +127,7 @@
         when(mDreamOverlayContainerView.getViewRootImpl()).thenReturn(mViewRoot);
         when(mDreamOverlayContainerView.getRootSurfaceControl())
                 .thenReturn(mAttachedSurfaceControl);
+        when(mKeyguardTransitionInteractor.isFinishedInStateWhere(any())).thenReturn(emptyFlow());
 
         mController = new DreamOverlayContainerViewController(
                 mDreamOverlayContainerView,
@@ -124,6 +137,7 @@
                 mLowLightTransitionCoordinator,
                 mBlurUtils,
                 mHandler,
+                mDispatcher,
                 mResources,
                 MAX_BURN_IN_OFFSET,
                 BURN_IN_PROTECTION_UPDATE_INTERVAL,
@@ -131,7 +145,8 @@
                 mPrimaryBouncerCallbackInteractor,
                 mAnimationsController,
                 mStateController,
-                mBouncerlessScrimController);
+                mBouncerlessScrimController,
+                mKeyguardTransitionInteractor);
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index b18a8ec..6a8ab39 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -35,6 +35,10 @@
 import com.android.systemui.SysuiTestCase
 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.ambient.touch.scrim.ScrimManager
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.complication.ComplicationHostViewController
 import com.android.systemui.complication.ComplicationLayoutEngine
 import com.android.systemui.complication.dagger.ComplicationComponent
@@ -43,6 +47,8 @@
 import com.android.systemui.touch.TouchInsetManager
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth
@@ -114,6 +120,14 @@
 
     @Mock lateinit var mUiEventLogger: UiEventLogger
 
+    @Mock lateinit var mScrimManager: ScrimManager
+
+    @Mock lateinit var mScrimController: ScrimController
+
+    @Mock lateinit var mCommunalInteractor: CommunalInteractor
+
+    @Mock lateinit var mSystemDialogsCloser: SystemDialogsCloser
+
     @Mock lateinit var mDreamOverlayCallbackController: DreamOverlayCallbackController
 
     @Captor var mViewCaptor: ArgumentCaptor<View>? = null
@@ -141,6 +155,7 @@
         whenever(mAmbientTouchComponent.getTouchMonitor()).thenReturn(mTouchMonitor)
         whenever(mDreamOverlayContainerViewController.containerView)
             .thenReturn(mDreamOverlayContainerView)
+        whenever(mScrimManager.getCurrentController()).thenReturn(mScrimController)
         mWindowParams = WindowManager.LayoutParams()
         mService =
             DreamOverlayService(
@@ -154,6 +169,9 @@
                 mAmbientTouchComponentFactory,
                 mStateController,
                 mKeyguardUpdateMonitor,
+                mScrimManager,
+                mCommunalInteractor,
+                mSystemDialogsCloser,
                 mUiEventLogger,
                 mTouchInsetManager,
                 LOW_LIGHT_COMPONENT,
@@ -563,6 +581,64 @@
             .isTrue()
     }
 
+    // Tests that the bouncer closes when DreamOverlayService is told that the dream is coming to
+    // the front.
+    @Test
+    fun testBouncerRetractedWhenDreamComesToFront() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            true /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+
+        whenever(mDreamOverlayContainerViewController.isBouncerShowing()).thenReturn(true)
+        mService!!.onComeToFront()
+        Mockito.verify(mScrimController).expand(any())
+    }
+
+    // Tests that glanceable hub is hidden when DreamOverlayService is told that the dream is
+    // coming to the front.
+    @Test
+    fun testGlanceableHubHiddenWhenDreamComesToFront() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            true /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+
+        mService!!.onComeToFront()
+        Mockito.verify(mCommunalInteractor).changeScene(eq(CommunalScenes.Blank), nullable())
+    }
+
+    // Tests that system dialogs (e.g. notification shade) closes when DreamOverlayService is told
+    // that the dream is coming to the front.
+    @Test
+    fun testSystemDialogsClosedWhenDreamComesToFront() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            true /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+
+        mService!!.onComeToFront()
+        Mockito.verify(mSystemDialogsCloser).closeSystemDialogs()
+    }
+
     companion object {
         private val LOW_LIGHT_COMPONENT = ComponentName("package", "lowlight")
         private val HOME_CONTROL_PANEL_DREAM_COMPONENT =
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index 5a174b9..f437032 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.communal
 
 import android.provider.Settings
+import android.service.dreams.Flags.dreamTracksFocus
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.CoreStartable
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
@@ -25,11 +26,13 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dock.DockManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.util.kotlin.emitOnStart
 import com.android.systemui.util.kotlin.sample
 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
@@ -37,15 +40,18 @@
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 /**
  * A [CoreStartable] responsible for automatically navigating between communal scenes when certain
@@ -61,8 +67,10 @@
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val keyguardInteractor: KeyguardInteractor,
     private val systemSettings: SystemSettings,
+    private val notificationShadeWindowController: NotificationShadeWindowController,
     @Application private val applicationScope: CoroutineScope,
     @Background private val bgScope: CoroutineScope,
+    @Main private val mainDispatcher: CoroutineDispatcher,
 ) : CoreStartable {
     private var screenTimeout: Int = DEFAULT_SCREEN_TIMEOUT
 
@@ -134,6 +142,16 @@
                     }
                 }
         }
+
+        if (dreamTracksFocus()) {
+            bgScope.launch {
+                communalInteractor.isIdleOnCommunal.collectLatest {
+                    withContext(mainDispatcher) {
+                        notificationShadeWindowController.setGlanceableHubShowing(it)
+                    }
+                }
+            }
+        }
     }
 
     private suspend fun determineSceneAfterTransition(
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 5a036b1..0c2709e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -22,6 +22,7 @@
 import static com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTOM;
 import static com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP;
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
 import android.animation.Animator;
 import android.content.res.Resources;
@@ -40,6 +41,8 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
 import com.android.systemui.dreams.dagger.DreamOverlayModule;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.res.R;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.statusbar.BlurUtils;
@@ -50,6 +53,8 @@
 import javax.inject.Inject;
 import javax.inject.Named;
 
+import kotlinx.coroutines.CoroutineDispatcher;
+
 /**
  * View controller for {@link DreamOverlayContainerView}.
  */
@@ -62,6 +67,7 @@
     private final DreamOverlayAnimationsController mDreamOverlayAnimationsController;
     private final DreamOverlayStateController mStateController;
     private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
+    private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
 
     private final ComplicationHostViewController mComplicationHostViewController;
 
@@ -81,6 +87,7 @@
 
     // Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
     private final Handler mHandler;
+    private final CoroutineDispatcher mMainDispatcher;
     private final int mDreamOverlayMaxTranslationY;
     private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
 
@@ -88,6 +95,7 @@
 
     private boolean mBouncerAnimating;
     private boolean mWakingUpFromSwipe;
+    private boolean mAnyBouncerShowing;
 
     private final BouncerlessScrimController mBouncerlessScrimController;
 
@@ -170,6 +178,7 @@
             LowLightTransitionCoordinator lowLightTransitionCoordinator,
             BlurUtils blurUtils,
             @Main Handler handler,
+            @Main CoroutineDispatcher mainDispatcher,
             @Main Resources resources,
             @Named(DreamOverlayModule.MAX_BURN_IN_OFFSET) int maxBurnInOffset,
             @Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
@@ -178,7 +187,8 @@
             PrimaryBouncerCallbackInteractor primaryBouncerCallbackInteractor,
             DreamOverlayAnimationsController animationsController,
             DreamOverlayStateController stateController,
-            BouncerlessScrimController bouncerlessScrimController) {
+            BouncerlessScrimController bouncerlessScrimController,
+            KeyguardTransitionInteractor keyguardTransitionInteractor) {
         super(containerView);
         mDreamOverlayContentView = contentView;
         mStatusBarViewController = statusBarViewController;
@@ -190,6 +200,8 @@
         mBouncerlessScrimController = bouncerlessScrimController;
         mBouncerlessScrimController.addCallback(mBouncerlessExpansionCallback);
 
+        mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+
         mComplicationHostViewController = complicationHostViewController;
         mDreamOverlayMaxTranslationY = resources.getDimensionPixelSize(
                 R.dimen.dream_overlay_y_offset);
@@ -200,6 +212,7 @@
                         ViewGroup.LayoutParams.MATCH_PARENT));
 
         mHandler = handler;
+        mMainDispatcher = mainDispatcher;
         mMaxBurnInOffset = maxBurnInOffset;
         mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval;
         mMillisUntilFullJitter = millisUntilFullJitter;
@@ -225,6 +238,12 @@
         mView.getRootSurfaceControl().setTouchableRegion(emptyRegion);
         emptyRegion.recycle();
 
+        collectFlow(
+                mView,
+                mKeyguardTransitionInteractor.isFinishedInStateWhere(KeyguardState::isBouncerState),
+                isFinished -> mAnyBouncerShowing = isFinished,
+                mMainDispatcher);
+
         // Start dream entry animations. Skip animations for low light clock.
         if (!mStateController.isLowLightActive()) {
             // If this is transitioning from the low light dream to the user dream, the overlay
@@ -246,6 +265,10 @@
         return mView;
     }
 
+    boolean isBouncerShowing() {
+        return mAnyBouncerShowing;
+    }
+
     private void updateBurnInOffsets() {
         // Make sure the offset starts at zero, to avoid a big jump in the overlay when it first
         // appears.
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 1135afe..3d52bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -45,10 +45,14 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.ambient.touch.TouchMonitor;
 import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent;
+import com.android.systemui.ambient.touch.scrim.ScrimManager;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.communal.shared.model.CommunalScenes;
 import com.android.systemui.complication.Complication;
 import com.android.systemui.complication.dagger.ComplicationComponent;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.touch.TouchInsetManager;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
@@ -76,6 +80,7 @@
     private DreamOverlayContainerViewController mDreamOverlayContainerViewController;
     private final DreamOverlayCallbackController mDreamOverlayCallbackController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final ScrimManager mScrimManager;
     @Nullable
     private final ComponentName mLowLightDreamComponent;
     @Nullable
@@ -107,6 +112,10 @@
 
     private TouchMonitor mTouchMonitor;
 
+    private final CommunalInteractor mCommunalInteractor;
+
+    private final SystemDialogsCloser mSystemDialogsCloser;
+
     private final KeyguardUpdateMonitorCallback mKeyguardCallback =
             new KeyguardUpdateMonitorCallback() {
                 @Override
@@ -168,6 +177,9 @@
             AmbientTouchComponent.Factory ambientTouchComponentFactory,
             DreamOverlayStateController stateController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
+            ScrimManager scrimManager,
+            CommunalInteractor communalInteractor,
+            SystemDialogsCloser systemDialogsCloser,
             UiEventLogger uiEventLogger,
             @Named(DREAM_TOUCH_INSET_MANAGER) TouchInsetManager touchInsetManager,
             @Nullable @Named(LowLightDreamModule.LOW_LIGHT_DREAM_COMPONENT)
@@ -181,6 +193,7 @@
         mExecutor = executor;
         mWindowManager = windowManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mScrimManager = scrimManager;
         mLowLightDreamComponent = lowLightDreamComponent;
         mHomeControlPanelDreamComponent = homeControlPanelDreamComponent;
         mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
@@ -188,6 +201,8 @@
         mUiEventLogger = uiEventLogger;
         mDreamOverlayCallbackController = dreamOverlayCallbackController;
         mWindowTitle = windowTitle;
+        mCommunalInteractor = communalInteractor;
+        mSystemDialogsCloser = systemDialogsCloser;
 
         final ViewModelStore viewModelStore = new ViewModelStore();
         final Complication.Host host =
@@ -292,6 +307,28 @@
         }
     }
 
+    @Override
+    public void onComeToFront() {
+        // Make sure the bouncer is closed. Expanding the shade effectively contracts the bouncer
+        // an equal amount.
+        if (mDreamOverlayContainerViewController != null
+                && mDreamOverlayContainerViewController.isBouncerShowing()) {
+            mScrimManager.getCurrentController().expand(
+                    new ShadeExpansionChangeEvent(
+                            /* fraction= */ 1.f,
+                            /* expanded= */ false,
+                            /* tracking= */ true));
+        }
+
+        // closeSystemDialogs takes care of closing anything that responds to the
+        // {@link Intent.ACTION_CLOSE_SYSTEM_DIALOGS} broadcast (which includes the notification
+        // shade).
+        mSystemDialogsCloser.closeSystemDialogs();
+
+        // Hide glanceable hub (this is a nop if glanceable hub is not open).
+        mCommunalInteractor.changeScene(CommunalScenes.Blank, null);
+    }
+
     /**
      * Inserts {@link Window} to host the dream overlay into the dream's parent window. Must be
      * called from the main executing thread. The window attributes closely mirror those that are
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/SystemDialogsCloser.java b/packages/SystemUI/src/com/android/systemui/dreams/SystemDialogsCloser.java
new file mode 100644
index 0000000..6e7239a4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/SystemDialogsCloser.java
@@ -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.dreams;
+
+/** Defines an interface for a class that is responsible for closing system dialogs. */
+public interface SystemDialogsCloser {
+    /** Close any open system dialogs. */
+    void closeSystemDialogs();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 31710ac..516b8c5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -31,6 +31,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.DreamOverlayNotificationCountProvider;
 import com.android.systemui.dreams.DreamOverlayService;
+import com.android.systemui.dreams.SystemDialogsCloser;
 import com.android.systemui.dreams.complication.dagger.ComplicationComponent;
 import com.android.systemui.dreams.homecontrols.DreamActivityProvider;
 import com.android.systemui.dreams.homecontrols.DreamActivityProviderImpl;
@@ -146,6 +147,15 @@
         return Optional.empty();
     }
 
+    /**
+     * Provides an implementation for {@link SystemDialogsCloser} that calls
+     * {@link Context.closeSystemDialogs}.
+     */
+    @Provides
+    static SystemDialogsCloser providesSystemDialogsCloser(Context context) {
+        return () -> context.closeSystemDialogs();
+    }
+
     /** */
     @Provides
     @Named(DREAM_ONLY_ENABLED_FOR_DOCK_USER)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index 92612b8..7d05539 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -87,6 +87,7 @@
         }
 
         /** Whether either of the bouncers are visible when we're FINISHED in the given state. */
+        @JvmStatic
         fun isBouncerState(state: KeyguardState): Boolean {
             return state == PRIMARY_BOUNCER || state == ALTERNATE_BOUNCER
         }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index adcb14a..8b7e11c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -448,6 +448,9 @@
                 || mScreenOffAnimationController.shouldIgnoreKeyguardTouches()) {
             mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
             mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+        } else if (state.glanceableHubShowing) {
+            mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
+            mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
             mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
             // Make sure to remove FLAG_ALT_FOCUSABLE_IM when keyguard needs input.
@@ -611,6 +614,7 @@
                 state.panelVisible,
                 state.shadeOrQsExpanded,
                 state.notificationShadeFocusable,
+                state.glanceableHubShowing,
                 state.bouncerShowing,
                 state.keyguardFadingAway,
                 state.keyguardGoingAway,
@@ -740,6 +744,12 @@
     }
 
     @Override
+    public void setGlanceableHubShowing(boolean showing) {
+        mCurrentState.glanceableHubShowing = showing;
+        apply(mCurrentState);
+    }
+
+    @Override
     public void setBackdropShowing(boolean showing) {
         mCurrentState.mediaBackdropShowing = showing;
         apply(mCurrentState);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
index e0a98b3..6a4b52a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
@@ -35,6 +35,7 @@
     @JvmField var shadeOrQsExpanded: Boolean = false,
     @JvmField var notificationShadeFocusable: Boolean = false,
     @JvmField var bouncerShowing: Boolean = false,
+    @JvmField var glanceableHubShowing: Boolean = false,
     @JvmField var keyguardFadingAway: Boolean = false,
     @JvmField var keyguardGoingAway: Boolean = false,
     @JvmField var qsExpanded: Boolean = false,
@@ -79,6 +80,7 @@
             shadeOrQsExpanded.toString(),
             notificationShadeFocusable.toString(),
             bouncerShowing.toString(),
+            glanceableHubShowing.toString(),
             keyguardFadingAway.toString(),
             keyguardGoingAway.toString(),
             qsExpanded.toString(),
@@ -119,6 +121,7 @@
             panelVisible: Boolean,
             panelExpanded: Boolean,
             notificationShadeFocusable: Boolean,
+            glanceableHubShowing: Boolean,
             bouncerShowing: Boolean,
             keyguardFadingAway: Boolean,
             keyguardGoingAway: Boolean,
@@ -149,6 +152,7 @@
                 this.panelVisible = panelVisible
                 this.shadeOrQsExpanded = panelExpanded
                 this.notificationShadeFocusable = notificationShadeFocusable
+                this.glanceableHubShowing = glanceableHubShowing
                 this.bouncerShowing = bouncerShowing
                 this.keyguardFadingAway = keyguardFadingAway
                 this.keyguardGoingAway = keyguardGoingAway
@@ -197,6 +201,7 @@
                 "panelVisible",
                 "panelExpanded",
                 "notificationShadeFocusable",
+                "glanceableHubShowing",
                 "bouncerShowing",
                 "keyguardFadingAway",
                 "keyguardGoingAway",
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index e669556..707d59aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -86,6 +86,9 @@
     /** Sets the state of whether the bouncer is showing or not. */
     default void setBouncerShowing(boolean showing) {}
 
+    /** Sets the state of whether the glanceable hub is showing or not. */
+    default void setGlanceableHubShowing(boolean showing) {}
+
     /** Sets the state of whether the backdrop is showing or not. */
     default void setBackdropShowing(boolean showing) {}
 
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 0e8a5fb..a818eab 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -249,6 +249,16 @@
         mCurrentDream.mAppTask = appTask;
     }
 
+    void setDreamHasFocus(boolean hasFocus) {
+        if (mCurrentDream != null) {
+            mCurrentDream.mDreamHasFocus = hasFocus;
+        }
+    }
+
+    boolean dreamHasFocus() {
+        return mCurrentDream != null && mCurrentDream.mDreamHasFocus;
+    }
+
     /**
      * Sends a user activity signal to PowerManager to stop the screen from turning off immediately
      * if there hasn't been any user interaction in a while.
@@ -271,6 +281,21 @@
         stopDreamInstance(immediate, reason, mCurrentDream);
     }
 
+    public boolean bringDreamToFront() {
+        if (mCurrentDream == null || mCurrentDream.mService == null) {
+            return false;
+        }
+
+        try {
+            mCurrentDream.mService.comeToFront();
+            return true;
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error asking dream to come to the front", e);
+        }
+
+        return false;
+    }
+
     /**
      * Stops the given dream instance.
      *
@@ -426,6 +451,7 @@
         private String mStopReason;
         private long mDreamStartTime;
         public boolean mWakingGently;
+        public boolean mDreamHasFocus;
 
         private final Runnable mStopPreviousDreamsIfNeeded = this::stopPreviousDreamsIfNeeded;
         private final Runnable mReleaseWakeLockIfNeeded = this::releaseWakeLockIfNeeded;
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 42c9e08..fc63494 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.service.dreams.Flags.dreamTracksFocus;
 
 import static com.android.server.wm.ActivityInterceptorCallback.DREAM_MANAGER_ORDERED_ID;
 
@@ -406,8 +407,10 @@
     /** Whether dreaming can start given user settings and the current dock/charge state. */
     private boolean canStartDreamingInternal(boolean isScreenOn) {
         synchronized (mLock) {
-            // Can't start dreaming if we are already dreaming.
-            if (isScreenOn && isDreamingInternal()) {
+            // Can't start dreaming if we are already dreaming and the dream has focus. If we are
+            // dreaming but the dream does not have focus, then the dream can be brought to the
+            // front so it does have focus.
+            if (isScreenOn && isDreamingInternal() && dreamHasFocus()) {
                 return false;
             }
 
@@ -442,11 +445,20 @@
         }
     }
 
+    private boolean dreamHasFocus() {
+        // Dreams always had focus before they were able to track it.
+        return !dreamTracksFocus() || mController.dreamHasFocus();
+    }
+
     protected void requestStartDreamFromShell() {
         requestDreamInternal();
     }
 
     private void requestDreamInternal() {
+        if (isDreamingInternal() && !dreamHasFocus() && mController.bringDreamToFront()) {
+            return;
+        }
+
         // Ask the power manager to nap.  It will eventually call back into
         // startDream() if/when it is appropriate to start dreaming.
         // Because napping could cause the screen to turn off immediately if the dream
@@ -1128,6 +1140,16 @@
             });
         }
 
+        @Override
+        public void onDreamFocusChanged(boolean hasFocus) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mController.setDreamHasFocus(hasFocus);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
         boolean canLaunchDreamActivity(String dreamPackageName, String packageName,
                 int callingUid) {
             if (dreamPackageName == null || packageName == null) {
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
index db70434..88ab871 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
@@ -19,6 +19,8 @@
 import static android.os.PowerManager.USER_ACTIVITY_EVENT_OTHER;
 import static android.os.PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -270,6 +272,31 @@
                 eq(USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS));
     }
 
+    @Test
+    public void setDreamHasFocus_true_dreamHasFocus() {
+        mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
+                0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
+
+        mDreamController.setDreamHasFocus(true);
+        assertTrue(mDreamController.dreamHasFocus());
+    }
+
+    @Test
+    public void setDreamHasFocus_false_dreamDoesNotHaveFocus() {
+        mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
+                0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
+
+        mDreamController.setDreamHasFocus(false);
+        assertFalse(mDreamController.dreamHasFocus());
+    }
+
+    @Test
+    public void setDreamHasFocus_notDreaming_dreamDoesNotHaveFocus() {
+        mDreamController.setDreamHasFocus(true);
+        // Dream still doesn't have focus because it was never started.
+        assertFalse(mDreamController.dreamHasFocus());
+    }
+
     private ServiceConnection captureServiceConnection() {
         verify(mContext).bindServiceAsUser(any(), mServiceConnectionACaptor.capture(), anyInt(),
                 any());