Merge "Create WM Shell Flicker helper library" into udc-qpr-dev
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 3a66081..c872516 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -3535,7 +3535,7 @@
      * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT } mode.
      * They can be queried through
      * {@link android.hardware.camera2.CameraCharacteristics#get } with
-     * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION) }.
+     * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION }.
      * Unless reported by both
      * {@link android.hardware.camera2.params.StreamConfigurationMap }s, the outputs from
      * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION android.scaler.streamConfigurationMapMaximumResolution}</code> and
@@ -3550,13 +3550,12 @@
      * <ul>
      * <li>
      * <p>The mandatory stream combinations listed in
-     *   {@link android.hardware.camera2.CameraCharacteristics.mandatoryMaximumResolutionStreamCombinations }
-     *   would not apply.</p>
+     *   {@link CameraCharacteristics#SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS android.scaler.mandatoryMaximumResolutionStreamCombinations}  would not apply.</p>
      * </li>
      * <li>
      * <p>The bayer pattern of {@code RAW} streams when
      *   {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
-     *   is selected will be the one listed in {@link android.sensor.info.binningFactor }.</p>
+     *   is selected will be the one listed in {@link CameraCharacteristics#SENSOR_INFO_BINNING_FACTOR android.sensor.info.binningFactor}.</p>
      * </li>
      * <li>
      * <p>The following keys will always be present:</p>
@@ -3576,9 +3575,11 @@
      *
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
      *
+     * @see CameraCharacteristics#SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS
      * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
      * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+     * @see CameraCharacteristics#SENSOR_INFO_BINNING_FACTOR
      * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
      * @see #SENSOR_PIXEL_MODE_DEFAULT
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 1536376..57f7bca 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -4460,7 +4460,7 @@
      * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT } mode.
      * They can be queried through
      * {@link android.hardware.camera2.CameraCharacteristics#get } with
-     * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION) }.
+     * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION }.
      * Unless reported by both
      * {@link android.hardware.camera2.params.StreamConfigurationMap }s, the outputs from
      * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION android.scaler.streamConfigurationMapMaximumResolution}</code> and
@@ -4475,13 +4475,12 @@
      * <ul>
      * <li>
      * <p>The mandatory stream combinations listed in
-     *   {@link android.hardware.camera2.CameraCharacteristics.mandatoryMaximumResolutionStreamCombinations }
-     *   would not apply.</p>
+     *   {@link CameraCharacteristics#SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS android.scaler.mandatoryMaximumResolutionStreamCombinations}  would not apply.</p>
      * </li>
      * <li>
      * <p>The bayer pattern of {@code RAW} streams when
      *   {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
-     *   is selected will be the one listed in {@link android.sensor.info.binningFactor }.</p>
+     *   is selected will be the one listed in {@link CameraCharacteristics#SENSOR_INFO_BINNING_FACTOR android.sensor.info.binningFactor}.</p>
      * </li>
      * <li>
      * <p>The following keys will always be present:</p>
@@ -4501,9 +4500,11 @@
      *
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
      *
+     * @see CameraCharacteristics#SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS
      * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
      * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+     * @see CameraCharacteristics#SENSOR_INFO_BINNING_FACTOR
      * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
      * @see #SENSOR_PIXEL_MODE_DEFAULT
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 17afd55..4ecfc40 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -844,12 +844,12 @@
     public boolean onStateChanged(InsetsState state) {
         boolean stateChanged = false;
         if (!CAPTION_ON_SHELL) {
-            stateChanged = !mState.equals(state, true /* excludingCaptionInsets */,
-                    false /* excludeInvisibleIme */)
+            stateChanged = !mState.equals(state, true /* excludesCaptionBar */,
+                    false /* excludesInvisibleIme */)
                     || captionInsetsUnchanged();
         } else {
-            stateChanged = !mState.equals(state, false /* excludingCaptionInsets */,
-                    false /* excludeInvisibleIme */);
+            stateChanged = !mState.equals(state, false /* excludesCaptionBar */,
+                    false /* excludesInvisibleIme */);
         }
         if (!stateChanged && mLastDispatchedState.equals(state)) {
             return false;
@@ -862,8 +862,8 @@
         applyLocalVisibilityOverride();
         updateCompatSysUiVisibility();
 
-        if (!mState.equals(lastState, false /* excludingCaptionInsets */,
-                true /* excludeInvisibleIme */)) {
+        if (!mState.equals(lastState, false /* excludesCaptionBar */,
+                true /* excludesInvisibleIme */)) {
             if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged");
             mHost.notifyInsetsChanged();
             if (lastState.getDisplayFrame().equals(mState.getDisplayFrame())) {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index c13b9ab..af24140 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -380,11 +380,17 @@
             @InternalInsetsSide @Nullable SparseIntArray idSideMap,
             @Nullable boolean[] typeVisibilityMap, Insets insets, int type) {
         int index = indexOf(type);
-        Insets existing = typeInsetsMap[index];
-        if (existing == null) {
-            typeInsetsMap[index] = insets;
-        } else {
-            typeInsetsMap[index] = Insets.max(existing, insets);
+
+        // Don't put Insets.NONE into typeInsetsMap. Otherwise, two WindowInsets can be considered
+        // as non-equal while they provide the same insets of each type from WindowInsets#getInsets
+        // if one WindowInsets has Insets.NONE for a type and the other has null for the same type.
+        if (!Insets.NONE.equals(insets)) {
+            Insets existing = typeInsetsMap[index];
+            if (existing == null) {
+                typeInsetsMap[index] = insets;
+            } else {
+                typeInsetsMap[index] = Insets.max(existing, insets);
+            }
         }
 
         if (typeVisibilityMap != null) {
@@ -696,15 +702,14 @@
      * An equals method can exclude the caption insets. This is useful because we assemble the
      * caption insets information on the client side, and when we communicate with server, it's
      * excluded.
-     * @param excludingCaptionInsets {@code true} if we want to compare two InsetsState objects but
-     *                                           ignore the caption insets source value.
-     * @param excludeInvisibleImeFrames If {@link WindowInsets.Type#ime()} frames should be ignored
-     *                                  when IME is not visible.
+     * @param excludesCaptionBar If {@link Type#captionBar()}} should be ignored.
+     * @param excludesInvisibleIme If {@link WindowInsets.Type#ime()} should be ignored when IME is
+     *                             not visible.
      * @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise.
      */
     @VisibleForTesting
-    public boolean equals(@Nullable Object o, boolean excludingCaptionInsets,
-            boolean excludeInvisibleImeFrames) {
+    public boolean equals(@Nullable Object o, boolean excludesCaptionBar,
+            boolean excludesInvisibleIme) {
         if (this == o) { return true; }
         if (o == null || getClass() != o.getClass()) { return false; }
 
@@ -721,29 +726,35 @@
 
         final SparseArray<InsetsSource> thisSources = mSources;
         final SparseArray<InsetsSource> thatSources = state.mSources;
-        if (!excludingCaptionInsets && !excludeInvisibleImeFrames) {
+        if (!excludesCaptionBar && !excludesInvisibleIme) {
             return thisSources.contentEquals(thatSources);
         } else {
             final int thisSize = thisSources.size();
             final int thatSize = thatSources.size();
             int thisIndex = 0;
             int thatIndex = 0;
-            while (thisIndex < thisSize && thatIndex < thatSize) {
+            while (thisIndex < thisSize || thatIndex < thatSize) {
+                InsetsSource thisSource = thisIndex < thisSize
+                        ? thisSources.valueAt(thisIndex)
+                        : null;
+
                 // Seek to the next non-excluding source of ours.
-                InsetsSource thisSource = thisSources.valueAt(thisIndex);
                 while (thisSource != null
-                        && (excludingCaptionInsets && thisSource.getType() == captionBar()
-                                || excludeInvisibleImeFrames && thisSource.getType() == ime()
+                        && (excludesCaptionBar && thisSource.getType() == captionBar()
+                                || excludesInvisibleIme && thisSource.getType() == ime()
                                         && !thisSource.isVisible())) {
                     thisIndex++;
                     thisSource = thisIndex < thisSize ? thisSources.valueAt(thisIndex) : null;
                 }
 
+                InsetsSource thatSource = thatIndex < thatSize
+                        ? thatSources.valueAt(thatIndex)
+                        : null;
+
                 // Seek to the next non-excluding source of theirs.
-                InsetsSource thatSource = thatSources.valueAt(thatIndex);
                 while (thatSource != null
-                        && (excludingCaptionInsets && thatSource.getType() == captionBar()
-                                || excludeInvisibleImeFrames && thatSource.getType() == ime()
+                        && (excludesCaptionBar && thatSource.getType() == captionBar()
+                                || excludesInvisibleIme && thatSource.getType() == ime()
                                         && !thatSource.isVisible())) {
                     thatIndex++;
                     thatSource = thatIndex < thatSize ? thatSources.valueAt(thatIndex) : null;
@@ -756,7 +767,7 @@
                 thisIndex++;
                 thatIndex++;
             }
-            return thisIndex >= thisSize && thatIndex >= thatSize;
+            return true;
         }
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 01a99b9..1b1098d 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2539,7 +2539,7 @@
         final int childrenCount = mChildrenCount;
         if (childrenCount != 0) {
             final float x = event.getXDispatchLocation(0);
-            final float y = event.getXDispatchLocation(0);
+            final float y = event.getYDispatchLocation(0);
 
             final ArrayList<View> preorderedList = buildOrderedChildList();
             final boolean customOrder = preorderedList == null
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 325ebbe..8e619a8 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -609,4 +609,6 @@
     optional bool animation_in_progress = 1;
     optional int32 last_back_type = 2;
     optional bool show_wallpaper = 3;
+    optional string main_open_activity = 4;
+    optional bool animation_running = 5;
 }
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index fde1a6d..b06cd39 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -43,6 +43,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -290,6 +291,18 @@
     }
 
     @Test
+    public void testCalculateInsets_emptyIme() {
+        WindowInsets insets1 = mState.calculateInsets(new Rect(), null, false, false,
+                SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+        mState.getOrCreateSource(ID_IME, ime());
+        WindowInsets insets2 = mState.calculateInsets(new Rect(), null, false, false,
+                SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+        assertEquals(Insets.NONE, insets1.getInsets(ime()));
+        assertEquals(Insets.NONE, insets2.getInsets(ime()));
+        assertEquals(insets1, insets2);
+    }
+
+    @Test
     public void testStripForDispatch() {
         mState.getOrCreateSource(ID_STATUS_BAR, statusBars())
                 .setFrame(new Rect(0, 0, 100, 100))
@@ -304,6 +317,73 @@
     }
 
     @Test
+    public void testEquals() {
+        final InsetsState state1 = new InsetsState();
+        final InsetsState state2 = new InsetsState();
+        assertTrue(state1.equals(state2));
+
+        state1.addSource(new InsetsSource(ID_STATUS_BAR, statusBars()));
+        assertFalse(state1.equals(state2));
+
+        state2.addSource(new InsetsSource(ID_STATUS_BAR, statusBars()));
+        assertTrue(state1.equals(state2));
+
+        state2.addSource(new InsetsSource(ID_NAVIGATION_BAR, navigationBars()));
+        assertFalse(state1.equals(state2));
+    }
+
+    @Test
+    public void testEquals_excludesCaptionBar() {
+        final InsetsState state1 = new InsetsState();
+        final InsetsState state2 = new InsetsState();
+
+        state1.addSource(new InsetsSource(ID_CAPTION_BAR, captionBar()).setFrame(0, 0, 0, 5));
+        assertFalse(state1.equals(
+                state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+        assertTrue(state1.equals(
+                state2, true /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+
+        state2.addSource(new InsetsSource(ID_CAPTION_BAR, captionBar()).setFrame(0, 0, 0, 10));
+        assertFalse(state1.equals(
+                state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+        assertTrue(state1.equals(
+                state2, true /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+
+        state1.addSource(new InsetsSource(ID_STATUS_BAR, statusBars()));
+        state2.addSource(new InsetsSource(ID_STATUS_BAR, statusBars()));
+        assertFalse(state1.equals(
+                state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+        assertTrue(state1.equals(
+                state2, true /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+    }
+
+    @Test
+    public void testEquals_excludesInvisibleIme() {
+        final InsetsState state1 = new InsetsState();
+        final InsetsState state2 = new InsetsState();
+
+        final InsetsSource imeSource1 = new InsetsSource(ID_IME, ime()).setVisible(true);
+        state1.addSource(imeSource1);
+        assertFalse(state1.equals(
+                state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+        assertFalse(state1.equals(
+                state2, false /* excludesCaptionBar */, true /* excludesInvisibleIme */));
+
+        imeSource1.setVisible(false);
+        assertFalse(state1.equals(
+                state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+        assertTrue(state1.equals(
+                state2, false /* excludesCaptionBar */, true /* excludesInvisibleIme */));
+
+        final InsetsSource imeSource2 = new InsetsSource(ID_IME, ime()).setFrame(0, 0, 0, 10);
+        state2.addSource(imeSource2);
+        assertFalse(state1.equals(
+                state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+        assertTrue(state1.equals(
+                state2, false /* excludesCaptionBar */, true /* excludesInvisibleIme */));
+    }
+
+    @Test
     public void testEquals_differentRect() {
         mState.getOrCreateSource(ID_STATUS_BAR, statusBars())
                 .setFrame(new Rect(0, 0, 100, 100));
@@ -404,17 +484,6 @@
     }
 
     @Test
-    public void testEquals_excludeInvisibleIme() {
-        mState.getOrCreateSource(ID_IME, ime())
-                .setFrame(new Rect(0, 0, 100, 100))
-                .setVisible(false);
-        mState2.getOrCreateSource(ID_IME, ime())
-                .setFrame(new Rect(0, 0, 100, 200))
-                .setVisible(false);
-        assertTrue(mState2.equals(mState, true, true /* excludeInvisibleIme */));
-    }
-
-    @Test
     public void testParcelUnparcel() {
         mState.getOrCreateSource(ID_IME, ime())
                 .setFrame(new Rect(0, 0, 100, 100))
diff --git a/core/tests/coretests/src/android/view/ViewGroupTest.java b/core/tests/coretests/src/android/view/ViewGroupTest.java
index b37c8fd..bce3f3e 100644
--- a/core/tests/coretests/src/android/view/ViewGroupTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupTest.java
@@ -49,11 +49,11 @@
     public void testDispatchMouseEventsUnderCursor() {
         final Context context = getInstrumentation().getContext();
         final TestView viewGroup = new TestView(context, 0 /* left */, 0 /* top */,
-                200 /* right */, 200 /* bottom */);
+                200 /* right */, 100 /* bottom */);
         final TestView viewA = spy(new TestView(context, 0 /* left */, 0 /* top */,
-                100 /* right */, 200 /* bottom */));
+                100 /* right */, 100 /* bottom */));
         final TestView viewB = spy(new TestView(context, 100 /* left */, 0 /* top */,
-                200 /* right */, 200 /* bottom */));
+                200 /* right */, 100 /* bottom */));
 
         viewGroup.addView(viewA);
         viewGroup.addView(viewB);
@@ -73,10 +73,10 @@
         MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[2];
         coords[0] = new MotionEvent.PointerCoords();
         coords[0].x = 80;
-        coords[0].y = 100;
+        coords[0].y = 50;
         coords[1] = new MotionEvent.PointerCoords();
         coords[1].x = 240;
-        coords[1].y = 100;
+        coords[1].y = 50;
 
         MotionEvent event;
         // Make sure the down event is active with a pointer which coordinate is different from the
@@ -91,6 +91,10 @@
         viewGroup.onResolvePointerIcon(event, 0 /* pointerIndex */);
         verify(viewB).onResolvePointerIcon(event, 0);
 
+        event.setAction(MotionEvent.ACTION_SCROLL);
+        viewGroup.dispatchGenericMotionEvent(event);
+        verify(viewB).dispatchGenericMotionEvent(event);
+
         event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
                 MotionEvent.ACTION_POINTER_DOWN | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT),
                 2 /* pointerCount */, properties, coords, 0 /* metaState */, 0 /* buttonState */,
@@ -102,8 +106,13 @@
         viewGroup.onResolvePointerIcon(event, 1 /* pointerIndex */);
         verify(viewB).onResolvePointerIcon(event, 1);
 
+        event.setAction(MotionEvent.ACTION_SCROLL);
+        viewGroup.dispatchGenericMotionEvent(event);
+        verify(viewB).dispatchGenericMotionEvent(event);
+
         verify(viewA, never()).dispatchTouchEvent(any());
         verify(viewA, never()).onResolvePointerIcon(any(), anyInt());
+        verify(viewA, never()).dispatchGenericMotionEvent(any());
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 04d999b..99c8acd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -817,13 +817,17 @@
      * @param interceptBack whether back should be intercepted or not.
      */
     void updateWindowFlagsForBackpress(boolean interceptBack) {
-        if (mStackView != null && mAddedToWindowManager) {
+        if (mAddedToWindowManager) {
             mWmLayoutParams.flags = interceptBack
                     ? 0
                     : WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                             | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
             mWmLayoutParams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-            mWindowManager.updateViewLayout(mStackView, mWmLayoutParams);
+            if (mStackView != null) {
+                mWindowManager.updateViewLayout(mStackView, mWmLayoutParams);
+            } else if (mLayerView != null) {
+                mWindowManager.updateViewLayout(mLayerView, mWmLayoutParams);
+            }
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 6718565..e698601 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -312,9 +312,13 @@
                         + " bubble=" + getBubbleKey());
             }
             if (mBubble != null) {
-                // Must post because this is called from a binder thread.
-                post(() -> mController.removeBubble(
-                        mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED));
+                mController.removeBubble(mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED);
+            }
+            if (mTaskView != null) {
+                // Release the surface
+                mTaskView.release();
+                removeView(mTaskView);
+                mTaskView = null;
             }
         }
 
@@ -1058,8 +1062,10 @@
     }
 
     /**
-     * Cleans up anything related to the task and {@code TaskView}. If this view should be reused
-     * after this method is called, then
+     * Cleans up anything related to the task. The TaskView itself is released after the task
+     * has been removed.
+     *
+     * If this view should be reused after this method is called, then
      * {@link #initialize(BubbleController, BubbleStackView, boolean)} must be invoked first.
      */
     public void cleanUpExpandedState() {
@@ -1081,10 +1087,7 @@
             }
         }
         if (mTaskView != null) {
-            // Release the surface & other task view related things
-            mTaskView.release();
-            removeView(mTaskView);
-            mTaskView = null;
+            mTaskView.setVisibility(GONE);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
index 76ca68b..517f9f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
@@ -54,6 +54,15 @@
     public static final boolean IS_DISPLAY_CHANGE_ENABLED = SystemProperties.getBoolean(
             "persist.wm.debug.desktop_change_display", false);
 
+
+    /**
+     * Flag to indicate that desktop stashing is enabled.
+     * When enabled, swiping home from desktop stashes the open apps. Next app that launches,
+     * will be added to the desktop.
+     */
+    private static final boolean IS_STASHING_ENABLED = SystemProperties.getBoolean(
+            "persist.wm.debug.desktop_stashing", false);
+
     /**
      * Return {@code true} if desktop mode support is enabled
      */
@@ -84,6 +93,13 @@
     }
 
     /**
+     * Return {@code true} if desktop task stashing is enabled when going home.
+     * Allows users to use home screen to add tasks to desktop.
+     */
+    public static boolean isStashingEnabled() {
+        return IS_STASHING_ENABLED;
+    }
+    /**
      * Check if desktop mode is active
      *
      * @return {@code true} if active
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 9bc28a46..b15fd91 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -150,20 +150,24 @@
      * back to front during the launch.
      */
     fun stashDesktopApps(displayId: Int) {
-        KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: stashDesktopApps")
-        desktopModeTaskRepository.setStashed(displayId, true)
+        if (DesktopModeStatus.isStashingEnabled()) {
+            KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: stashDesktopApps")
+            desktopModeTaskRepository.setStashed(displayId, true)
+        }
     }
 
     /**
      * Clear the stashed state for the given display
      */
     fun hideStashedDesktopApps(displayId: Int) {
-        KtProtoLog.v(
-                WM_SHELL_DESKTOP_MODE,
-                "DesktopTasksController: hideStashedApps displayId=%d",
-                displayId
-        )
-        desktopModeTaskRepository.setStashed(displayId, false)
+        if (DesktopModeStatus.isStashingEnabled()) {
+            KtProtoLog.v(
+                    WM_SHELL_DESKTOP_MODE,
+                    "DesktopTasksController: hideStashedApps displayId=%d",
+                    displayId
+            )
+            desktopModeTaskRepository.setStashed(displayId, false)
+        }
     }
 
     /** Get number of tasks that are marked as visible */
@@ -172,9 +176,13 @@
     }
 
     /** Move a task with given `taskId` to desktop */
-    fun moveToDesktop(taskId: Int, wct: WindowContainerTransaction = WindowContainerTransaction()) {
+    fun moveToDesktop(
+            decor: DesktopModeWindowDecoration,
+            taskId: Int,
+            wct: WindowContainerTransaction = WindowContainerTransaction()
+    ) {
         shellTaskOrganizer.getRunningTaskInfo(taskId)?.let {
-            task -> moveToDesktop(task, wct)
+            task -> moveToDesktop(decor, task, wct)
         }
     }
 
@@ -182,6 +190,7 @@
      * Move a task to desktop
      */
     fun moveToDesktop(
+            decor: DesktopModeWindowDecoration,
             task: RunningTaskInfo,
             wct: WindowContainerTransaction = WindowContainerTransaction()
     ) {
@@ -195,7 +204,7 @@
         addMoveToDesktopChanges(wct, task)
 
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
+            enterDesktopTaskTransitionHandler.moveToDesktop(wct, decor)
         } else {
             shellTaskOrganizer.applyTransaction(wct)
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
index 22929c76..16b2393 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
@@ -20,6 +20,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.RectEvaluator;
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
 import android.graphics.PointF;
@@ -36,6 +37,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration;
 import com.android.wm.shell.windowdecor.MoveToDesktopAnimator;
 
 import java.util.ArrayList;
@@ -60,6 +62,7 @@
     private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
     private Consumer<SurfaceControl.Transaction> mOnAnimationFinishedCallback;
     private MoveToDesktopAnimator mMoveToDesktopAnimator;
+    private DesktopModeWindowDecoration mDesktopModeWindowDecoration;
 
     public EnterDesktopTaskTransitionHandler(
             Transitions transitions) {
@@ -128,6 +131,18 @@
                 onAnimationEndCallback);
     }
 
+    /**
+     * Starts Transition of type TRANSIT_MOVE_TO_DESKTOP
+     * @param wct WindowContainerTransaction for transition
+     * @param decor {@link DesktopModeWindowDecoration} of task being animated
+     */
+    public void moveToDesktop(@NonNull WindowContainerTransaction wct,
+            DesktopModeWindowDecoration decor) {
+        mDesktopModeWindowDecoration = decor;
+        startTransition(Transitions.TRANSIT_MOVE_TO_DESKTOP, wct,
+                null /* onAnimationEndCallback */);
+    }
+
     @Override
     public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startT,
@@ -167,138 +182,209 @@
         }
 
         final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
-        if (type == Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE
+        if (type == Transitions.TRANSIT_MOVE_TO_DESKTOP
                 && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
-            // Transitioning to freeform but keeping fullscreen bounds, so the crop is set
-            // to null and we don't require an animation
-            final SurfaceControl sc = change.getLeash();
-            startT.setWindowCrop(sc, null);
-
-            if (mMoveToDesktopAnimator == null
-                    || mMoveToDesktopAnimator.getTaskId() != change.getTaskInfo().taskId) {
-                Slog.e(TAG, "No animator available for this transition");
-                return false;
-            }
-
-            // Calculate and set position of the task
-            final PointF position = mMoveToDesktopAnimator.getPosition();
-            startT.setPosition(sc, position.x, position.y);
-            finishT.setPosition(sc, position.x, position.y);
-
-            startT.apply();
-
-            mTransitions.getMainExecutor().execute(
-                    () -> finishCallback.onTransitionFinished(null, null));
-
-            return true;
+            return animateMoveToDesktop(change, startT, finishCallback);
         }
 
-        Rect endBounds = change.getEndAbsBounds();
+        if (type == Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE
+                && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+            return animateStartDragToDesktopMode(change, startT, finishT, finishCallback);
+        }
+
+        final Rect endBounds = change.getEndAbsBounds();
         if (type == Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
                 && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
                 && !endBounds.isEmpty()) {
-            // This Transition animates a task to freeform bounds after being dragged into freeform
-            // mode and brings the remaining freeform tasks to front
-            final SurfaceControl sc = change.getLeash();
-            startT.setWindowCrop(sc, endBounds.width(),
-                    endBounds.height());
-            startT.apply();
-
-            // End the animation that shrinks the window when task is first dragged from fullscreen
-            if (mMoveToDesktopAnimator != null) {
-                mMoveToDesktopAnimator.endAnimator();
-            }
-
-            // We want to find the scale of the current bounds relative to the end bounds. The
-            // task is currently scaled to DRAG_FREEFORM_SCALE and the final bounds will be
-            // scaled to FINAL_FREEFORM_SCALE. So, it is scaled to
-            // DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE relative to the freeform bounds
-            final ValueAnimator animator =
-                    ValueAnimator.ofFloat(
-                            MoveToDesktopAnimator.DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE, 1f);
-            animator.setDuration(FREEFORM_ANIMATION_DURATION);
-            final SurfaceControl.Transaction t = mTransactionSupplier.get();
-            animator.addUpdateListener(animation -> {
-                final float animationValue = (float) animation.getAnimatedValue();
-                t.setScale(sc, animationValue, animationValue);
-
-                final float animationWidth = endBounds.width() * animationValue;
-                final float animationHeight = endBounds.height() * animationValue;
-                final int animationX = endBounds.centerX() - (int) (animationWidth / 2);
-                final int animationY = endBounds.centerY() - (int) (animationHeight / 2);
-
-                t.setPosition(sc, animationX, animationY);
-                t.apply();
-            });
-
-            animator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (mOnAnimationFinishedCallback != null) {
-                        mOnAnimationFinishedCallback.accept(finishT);
-                    }
-                    mTransitions.getMainExecutor().execute(
-                            () -> finishCallback.onTransitionFinished(null, null));
-                }
-            });
-
-            animator.start();
-            return true;
+            return animateFinalizeDragToDesktopMode(change, startT, finishT, finishCallback,
+                    endBounds);
         }
 
         if (type == Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE
                 && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
-            // This Transition animates a task to fullscreen after being dragged from the status
-            // bar and then released back into the status bar area
-            final SurfaceControl sc = change.getLeash();
-            // Hide the first (fullscreen) frame because the animation will start from the smaller
-            // scale size.
-            startT.hide(sc)
-                    .setWindowCrop(sc, endBounds.width(), endBounds.height())
-                    .apply();
-
-            if (mMoveToDesktopAnimator == null
-                    || mMoveToDesktopAnimator.getTaskId() != change.getTaskInfo().taskId) {
-                Slog.e(TAG, "No animator available for this transition");
-                return false;
-            }
-
-            // End the animation that shrinks the window when task is first dragged from fullscreen
-            mMoveToDesktopAnimator.endAnimator();
-
-            final ValueAnimator animator = new ValueAnimator();
-            animator.setFloatValues(MoveToDesktopAnimator.DRAG_FREEFORM_SCALE, 1f);
-            animator.setDuration(FREEFORM_ANIMATION_DURATION);
-            final SurfaceControl.Transaction t = mTransactionSupplier.get();
-
-            // Get position of the task
-            final float x = mMoveToDesktopAnimator.getPosition().x;
-            final float y = mMoveToDesktopAnimator.getPosition().y;
-
-            animator.addUpdateListener(animation -> {
-                final float scale = (float) animation.getAnimatedValue();
-                t.setPosition(sc, x * (1 - scale), y * (1 - scale))
-                        .setScale(sc, scale, scale)
-                        .show(sc)
-                        .apply();
-            });
-            animator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (mOnAnimationFinishedCallback != null) {
-                        mOnAnimationFinishedCallback.accept(finishT);
-                    }
-                    mTransitions.getMainExecutor().execute(
-                            () -> finishCallback.onTransitionFinished(null, null));
-                }
-            });
-            animator.start();
-            return true;
+            return animateCancelDragToDesktopMode(change, startT, finishT, finishCallback,
+                    endBounds);
         }
 
         return false;
     }
 
+    private boolean animateMoveToDesktop(
+            @NonNull TransitionInfo.Change change,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        if (mDesktopModeWindowDecoration == null) {
+            Slog.e(TAG, "Window Decoration is not available for this transition");
+            return false;
+        }
+
+        final SurfaceControl leash = change.getLeash();
+        final Rect startBounds = change.getStartAbsBounds();
+        startT.setPosition(leash, startBounds.left, startBounds.right)
+                .setWindowCrop(leash, startBounds.width(), startBounds.height())
+                .show(leash);
+        mDesktopModeWindowDecoration.showResizeVeil(startT, startBounds);
+
+        final ValueAnimator animator = ValueAnimator.ofObject(new RectEvaluator(),
+                change.getStartAbsBounds(), change.getEndAbsBounds());
+        animator.setDuration(FREEFORM_ANIMATION_DURATION);
+        SurfaceControl.Transaction t = mTransactionSupplier.get();
+        animator.addUpdateListener(animation -> {
+            final Rect animationValue = (Rect) animator.getAnimatedValue();
+            t.setPosition(leash, animationValue.left, animationValue.right)
+                    .setWindowCrop(leash, animationValue.width(), animationValue.height())
+                    .show(leash);
+            mDesktopModeWindowDecoration.updateResizeVeil(t, animationValue);
+        });
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mDesktopModeWindowDecoration.hideResizeVeil();
+                mTransitions.getMainExecutor().execute(
+                        () -> finishCallback.onTransitionFinished(null, null));
+            }
+        });
+        animator.start();
+        return true;
+    }
+
+    private boolean animateStartDragToDesktopMode(
+            @NonNull TransitionInfo.Change change,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        // Transitioning to freeform but keeping fullscreen bounds, so the crop is set
+        // to null and we don't require an animation
+        final SurfaceControl sc = change.getLeash();
+        startT.setWindowCrop(sc, null);
+
+        if (mMoveToDesktopAnimator == null
+                || mMoveToDesktopAnimator.getTaskId() != change.getTaskInfo().taskId) {
+            Slog.e(TAG, "No animator available for this transition");
+            return false;
+        }
+
+        // Calculate and set position of the task
+        final PointF position = mMoveToDesktopAnimator.getPosition();
+        startT.setPosition(sc, position.x, position.y);
+        finishT.setPosition(sc, position.x, position.y);
+
+        startT.apply();
+
+        mTransitions.getMainExecutor().execute(
+                () -> finishCallback.onTransitionFinished(null, null));
+
+        return true;
+    }
+
+    private boolean animateFinalizeDragToDesktopMode(
+            @NonNull TransitionInfo.Change change,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            @NonNull Transitions.TransitionFinishCallback finishCallback,
+            @NonNull Rect endBounds) {
+        // This Transition animates a task to freeform bounds after being dragged into freeform
+        // mode and brings the remaining freeform tasks to front
+        final SurfaceControl sc = change.getLeash();
+        startT.setWindowCrop(sc, endBounds.width(),
+                endBounds.height());
+        startT.apply();
+
+        // End the animation that shrinks the window when task is first dragged from fullscreen
+        if (mMoveToDesktopAnimator != null) {
+            mMoveToDesktopAnimator.endAnimator();
+        }
+
+        // We want to find the scale of the current bounds relative to the end bounds. The
+        // task is currently scaled to DRAG_FREEFORM_SCALE and the final bounds will be
+        // scaled to FINAL_FREEFORM_SCALE. So, it is scaled to
+        // DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE relative to the freeform bounds
+        final ValueAnimator animator =
+                ValueAnimator.ofFloat(
+                        MoveToDesktopAnimator.DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE, 1f);
+        animator.setDuration(FREEFORM_ANIMATION_DURATION);
+        final SurfaceControl.Transaction t = mTransactionSupplier.get();
+        animator.addUpdateListener(animation -> {
+            final float animationValue = (float) animation.getAnimatedValue();
+            t.setScale(sc, animationValue, animationValue);
+
+            final float animationWidth = endBounds.width() * animationValue;
+            final float animationHeight = endBounds.height() * animationValue;
+            final int animationX = endBounds.centerX() - (int) (animationWidth / 2);
+            final int animationY = endBounds.centerY() - (int) (animationHeight / 2);
+
+            t.setPosition(sc, animationX, animationY);
+            t.apply();
+        });
+
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mOnAnimationFinishedCallback != null) {
+                    mOnAnimationFinishedCallback.accept(finishT);
+                }
+                mTransitions.getMainExecutor().execute(
+                        () -> finishCallback.onTransitionFinished(null, null));
+            }
+        });
+
+        animator.start();
+        return true;
+    }
+    private boolean animateCancelDragToDesktopMode(
+            @NonNull TransitionInfo.Change change,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            @NonNull Transitions.TransitionFinishCallback finishCallback,
+            @NonNull Rect endBounds) {
+        // This Transition animates a task to fullscreen after being dragged from the status
+        // bar and then released back into the status bar area
+        final SurfaceControl sc = change.getLeash();
+        // Hide the first (fullscreen) frame because the animation will start from the smaller
+        // scale size.
+        startT.hide(sc)
+                .setWindowCrop(sc, endBounds.width(), endBounds.height())
+                .apply();
+
+        if (mMoveToDesktopAnimator == null
+                || mMoveToDesktopAnimator.getTaskId() != change.getTaskInfo().taskId) {
+            Slog.e(TAG, "No animator available for this transition");
+            return false;
+        }
+
+        // End the animation that shrinks the window when task is first dragged from fullscreen
+        mMoveToDesktopAnimator.endAnimator();
+
+        final ValueAnimator animator = new ValueAnimator();
+        animator.setFloatValues(MoveToDesktopAnimator.DRAG_FREEFORM_SCALE, 1f);
+        animator.setDuration(FREEFORM_ANIMATION_DURATION);
+        final SurfaceControl.Transaction t = mTransactionSupplier.get();
+
+        // Get position of the task
+        final float x = mMoveToDesktopAnimator.getPosition().x;
+        final float y = mMoveToDesktopAnimator.getPosition().y;
+
+        animator.addUpdateListener(animation -> {
+            final float scale = (float) animation.getAnimatedValue();
+            t.setPosition(sc, x * (1 - scale), y * (1 - scale))
+                    .setScale(sc, scale, scale)
+                    .show(sc)
+                    .apply();
+        });
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mOnAnimationFinishedCallback != null) {
+                    mOnAnimationFinishedCallback.accept(finishT);
+                }
+                mTransitions.getMainExecutor().execute(
+                        () -> finishCallback.onTransitionFinished(null, null));
+            }
+        });
+        animator.start();
+        return true;
+    }
+
     @Nullable
     @Override
     public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 064af04..a743e99 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -317,18 +317,12 @@
         // we know about -- so leave clean-up here even if shell transitions are enabled.
         if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
 
-        if (mListener != null) {
-            final int taskId = taskInfo.taskId;
-            mListenerExecutor.execute(() -> {
-                mListener.onTaskRemovalStarted(taskId);
-            });
-        }
-        mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
+        final SurfaceControl taskLeash = mTaskLeash;
+        handleAndNotifyTaskRemoval(mTaskInfo);
 
         // Unparent the task when this surface is destroyed
-        mTransaction.reparent(mTaskLeash, null).apply();
+        mTransaction.reparent(taskLeash, null).apply();
         resetTaskInfo();
-        mTaskViewBase.onTaskVanished(taskInfo);
     }
 
     @Override
@@ -498,6 +492,20 @@
         }
     }
 
+    /** Notifies listeners of a task being removed and stops intercepting back presses on it. */
+    private void handleAndNotifyTaskRemoval(ActivityManager.RunningTaskInfo taskInfo) {
+        if (taskInfo != null) {
+            if (mListener != null) {
+                final int taskId = taskInfo.taskId;
+                mListenerExecutor.execute(() -> {
+                    mListener.onTaskRemovalStarted(taskId);
+                });
+            }
+            mTaskViewBase.onTaskVanished(taskInfo);
+            mTaskOrganizer.setInterceptBackPressedOnTaskRoot(taskInfo.token, false);
+        }
+    }
+
     /** Returns the task info for the task in the TaskView. */
     @Nullable
     public ActivityManager.RunningTaskInfo getTaskInfo() {
@@ -523,18 +531,12 @@
      */
     void cleanUpPendingTask() {
         if (mPendingInfo != null) {
-            if (mListener != null) {
-                final int taskId = mPendingInfo.taskId;
-                mListenerExecutor.execute(() -> {
-                    mListener.onTaskRemovalStarted(taskId);
-                });
-            }
-            mTaskViewBase.onTaskVanished(mPendingInfo);
-            mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mPendingInfo.token, false);
+            final ActivityManager.RunningTaskInfo pendingInfo = mPendingInfo;
+            handleAndNotifyTaskRemoval(pendingInfo);
 
             // Make sure the task is removed
             WindowContainerTransaction wct = new WindowContainerTransaction();
-            wct.removeTask(mPendingInfo.token);
+            wct.removeTask(pendingInfo.token);
             mTaskViewTransitions.closeTaskView(wct, this);
         }
         resetTaskInfo();
@@ -559,16 +561,7 @@
      * is used instead.
      */
     void prepareCloseAnimation() {
-        if (mTaskToken != null) {
-            if (mListener != null) {
-                final int taskId = mTaskInfo.taskId;
-                mListenerExecutor.execute(() -> {
-                    mListener.onTaskRemovalStarted(taskId);
-                });
-            }
-            mTaskViewBase.onTaskVanished(mTaskInfo);
-            mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
-        }
+        handleAndNotifyTaskRemoval(mTaskInfo);
         resetTaskInfo();
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index e45dacf..e2dce88 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -168,6 +168,9 @@
     public static final int TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE =
             WindowManager.TRANSIT_FIRST_CUSTOM + 14;
 
+    /** Transition to animate task to desktop. */
+    public static final int TRANSIT_MOVE_TO_DESKTOP = WindowManager.TRANSIT_FIRST_CUSTOM + 15;
+
     private final WindowOrganizer mOrganizer;
     private final Context mContext;
     private final ShellExecutor mMainExecutor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index ea9976d..4cc755b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -222,7 +222,8 @@
                 && (info.getType() == Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
                 || info.getType() == Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE
                 || info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE
-                || info.getType() == Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE)) {
+                || info.getType() == Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE
+                || info.getType() == Transitions.TRANSIT_MOVE_TO_DESKTOP)) {
             mWindowDecorByTaskId.get(change.getTaskInfo().taskId)
                     .addTransitionPausingRelayout(transition);
         }
@@ -356,7 +357,8 @@
                     // App sometimes draws before the insets from WindowDecoration#relayout have
                     // been added, so they must be added here
                     mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct);
-                    mDesktopTasksController.get().moveToDesktop(mTaskId, wct);
+                    decoration.incrementRelayoutBlock();
+                    mDesktopTasksController.get().moveToDesktop(decoration, mTaskId, wct);
                 }
                 decoration.closeHandleMenu();
             } else if (id == R.id.fullscreen_button) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 1477cf7..5d87cf8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -57,6 +57,7 @@
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.After
@@ -92,6 +93,7 @@
     @Mock lateinit var mToggleResizeDesktopTaskTransitionHandler:
             ToggleResizeDesktopTaskTransitionHandler
     @Mock lateinit var launchAdjacentController: LaunchAdjacentController
+    @Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var controller: DesktopTasksController
@@ -276,8 +278,8 @@
     fun moveToDesktop_displayFullscreen_windowingModeSetToFreeform() {
         val task = setUpFullscreenTask()
         task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
-        controller.moveToDesktop(task)
-        val wct = getLatestWct(expectTransition = TRANSIT_CHANGE)
+        controller.moveToDesktop(desktopModeWindowDecoration, task)
+        val wct = getLatestMoveToDesktopWct()
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
             .isEqualTo(WINDOWING_MODE_FREEFORM)
     }
@@ -286,15 +288,15 @@
     fun moveToDesktop_displayFreeform_windowingModeSetToUndefined() {
         val task = setUpFullscreenTask()
         task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM
-        controller.moveToDesktop(task)
-        val wct = getLatestWct(expectTransition = TRANSIT_CHANGE)
+        controller.moveToDesktop(desktopModeWindowDecoration, task)
+        val wct = getLatestMoveToDesktopWct()
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
                 .isEqualTo(WINDOWING_MODE_UNDEFINED)
     }
 
     @Test
     fun moveToDesktop_nonExistentTask_doesNothing() {
-        controller.moveToDesktop(999)
+        controller.moveToDesktop(desktopModeWindowDecoration, 999)
         verifyWCTNotExecuted()
     }
 
@@ -305,9 +307,9 @@
         val fullscreenTask = setUpFullscreenTask()
         markTaskHidden(freeformTask)
 
-        controller.moveToDesktop(fullscreenTask)
+        controller.moveToDesktop(desktopModeWindowDecoration, fullscreenTask)
 
-        with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+        with(getLatestMoveToDesktopWct()) {
             // Operations should include home task, freeform task
             assertThat(hierarchyOps).hasSize(3)
             assertReorderSequence(homeTask, freeformTask, fullscreenTask)
@@ -327,9 +329,9 @@
         val freeformTaskSecond = setUpFreeformTask(displayId = SECOND_DISPLAY)
         markTaskHidden(freeformTaskSecond)
 
-        controller.moveToDesktop(fullscreenTaskDefault)
+        controller.moveToDesktop(desktopModeWindowDecoration, fullscreenTaskDefault)
 
-        with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+        with(getLatestMoveToDesktopWct()) {
             // Check that hierarchy operations do not include tasks from second display
             assertThat(hierarchyOps.map { it.container })
                 .doesNotContain(homeTaskSecond.token.asBinder())
@@ -498,6 +500,7 @@
     @Test
     fun handleRequest_fullscreenTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() {
         assumeTrue(ENABLE_SHELL_TRANSITIONS)
+        whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
 
         val stashedFreeformTask = setUpFreeformTask(DEFAULT_DISPLAY)
         markTaskHidden(stashedFreeformTask)
@@ -569,6 +572,7 @@
     @Test
     fun handleRequest_freeformTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() {
         assumeTrue(ENABLE_SHELL_TRANSITIONS)
+        whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
 
         val stashedFreeformTask = setUpFreeformTask(DEFAULT_DISPLAY)
         markTaskHidden(stashedFreeformTask)
@@ -626,6 +630,8 @@
 
     @Test
     fun stashDesktopApps_stateUpdates() {
+        whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
+
         controller.stashDesktopApps(DEFAULT_DISPLAY)
 
         assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isTrue()
@@ -634,6 +640,8 @@
 
     @Test
     fun hideStashedDesktopApps_stateUpdates() {
+        whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
+
         desktopModeTaskRepository.setStashed(DEFAULT_DISPLAY, true)
         desktopModeTaskRepository.setStashed(SECOND_DISPLAY, true)
         controller.hideStashedDesktopApps(DEFAULT_DISPLAY)
@@ -715,6 +723,16 @@
         return arg.value
     }
 
+    private fun getLatestMoveToDesktopWct(): WindowContainerTransaction {
+        val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        if (ENABLE_SHELL_TRANSITIONS) {
+            verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any())
+        } else {
+            verify(shellTaskOrganizer).applyTransaction(arg.capture())
+        }
+        return arg.value
+    }
+
     private fun verifyWCTNotExecuted() {
         if (ENABLE_SHELL_TRANSITIONS) {
             verify(transitions, never()).startTransition(anyInt(), any(), isNull())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index 50435a0..d098d33 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -607,4 +607,29 @@
         verify(mTaskViewTaskController).applyCaptionInsetsIfNeeded();
         verify(mOrganizer).applyTransaction(any());
     }
+
+    @Test
+    public void testReleaseInOnTaskRemoval_noNPE() {
+        mTaskViewTaskController = spy(new TaskViewTaskController(mContext, mOrganizer,
+                mTaskViewTransitions, mSyncQueue));
+        mTaskView = new TaskView(mContext, mTaskViewTaskController);
+        mTaskView.setListener(mExecutor, new TaskView.Listener() {
+            @Override
+            public void onTaskRemovalStarted(int taskId) {
+                mTaskView.release();
+            }
+        });
+
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+                new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+                mLeash, wct);
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+
+        assertThat(mTaskViewTaskController.getTaskInfo()).isEqualTo(mTaskInfo);
+
+        mTaskViewTaskController.prepareCloseAnimation();
+
+        assertThat(mTaskViewTaskController.getTaskInfo()).isNull();
+    }
 }
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 2711dad..3af7a45 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -56,20 +56,6 @@
       ]
     },
     {
-      "name": "SystemUIGoogleScreenshotTests",
-      "options": [
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "android.platform.test.annotations.Postsubmit"
-        }
-      ]
-    },
-    {
       // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+)
       "name": "SystemUIGoogleBiometricsScreenshotTests",
       "options": [
@@ -151,21 +137,5 @@
         }
       ]
     }
-  ],
-  "postsubmit": [
-   {
-      "name": "SystemUIGoogleScreenshotTests",
-      "options": [
-        {
-            "exclude-annotation": "org.junit.Ignore"
-        },
-        {
-            "exclude-annotation": "android.platform.test.annotations.FlakyTest"
-        },
-        {
-            "include-annotation": "android.platform.test.annotations.Postsubmit"
-        }
-      ]
-    }
   ]
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 6d9497d..b9baa793 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -46,6 +46,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.R
 import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
@@ -81,7 +82,7 @@
             .asStateFlow()
 
     @Composable
-    override fun Content(
+    override fun SceneScope.Content(
         modifier: Modifier,
     ) = BouncerScene(viewModel, dialogFactory, modifier)
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index ab7bc26..ca7352e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -29,6 +29,7 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.dagger.SysUISingleton
@@ -66,7 +67,7 @@
             )
 
     @Composable
-    override fun Content(
+    override fun SceneScope.Content(
         modifier: Modifier,
     ) {
         LockscreenScene(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 130395a..29763c2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -27,6 +27,7 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel
 import com.android.systemui.scene.shared.model.Direction
@@ -57,7 +58,7 @@
             .asStateFlow()
 
     @Composable
-    override fun Content(
+    override fun SceneScope.Content(
         modifier: Modifier,
     ) {
         QuickSettingsScene(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt
index a213666..3da6a02 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt
@@ -18,9 +18,10 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.scene.shared.model.Scene
 
 /** Compose-capable extension of [Scene]. */
 interface ComposableScene : Scene {
-    @Composable fun Content(modifier: Modifier)
+    @Composable fun SceneScope.Content(modifier: Modifier)
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 0070552..774c409 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -23,6 +23,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.scene.shared.model.Direction
 import com.android.systemui.scene.shared.model.SceneKey
@@ -50,7 +51,7 @@
             .asStateFlow()
 
     @Composable
-    override fun Content(
+    override fun SceneScope.Content(
         modifier: Modifier,
     ) {
         /*
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 49e2bf9..3dfdbba 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -14,32 +14,31 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalAnimationApi::class)
-
 package com.android.systemui.scene.ui.composable
 
-import androidx.activity.compose.BackHandler
-import androidx.compose.animation.AnimatedContent
-import androidx.compose.animation.ExperimentalAnimationApi
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.material3.Button
-import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.ObservableTransitionState as SceneTransitionObservableTransitionState
+import com.android.compose.animation.scene.SceneKey as SceneTransitionSceneKey
+import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.compose.animation.scene.SceneTransitionLayoutState
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserAction as SceneTransitionUserAction
+import com.android.compose.animation.scene.observableTransitionState
+import com.android.compose.animation.scene.transitions
 import com.android.systemui.scene.shared.model.Direction
+import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.scene.shared.model.UserAction
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import java.util.Locale
+import kotlinx.coroutines.flow.map
 
 /**
  * Renders a container of a collection of "scenes" that the user can switch between using certain
@@ -64,77 +63,94 @@
     sceneByKey: Map<SceneKey, ComposableScene>,
     modifier: Modifier = Modifier,
 ) {
-    val currentScene: SceneModel by viewModel.currentScene.collectAsState()
+    val currentSceneModel: SceneModel by viewModel.currentScene.collectAsState()
+    val currentSceneKey = currentSceneModel.key
+    val currentScene = checkNotNull(sceneByKey[currentSceneKey])
+    val currentDestinations: Map<UserAction, SceneModel> by
+        currentScene.destinationScenes().collectAsState()
+    val state = remember { SceneTransitionLayoutState(currentSceneKey.toTransitionSceneKey()) }
 
-    AnimatedContent(
-        targetState = currentScene.key,
-        label = "scene container",
-        modifier = modifier,
-    ) { currentSceneKey ->
-        sceneByKey.forEach { (key, composableScene) ->
-            if (key == currentSceneKey) {
-                Scene(
-                    scene = composableScene,
-                    onSceneChanged = viewModel::setCurrentScene,
-                    modifier = Modifier.fillMaxSize(),
-                )
-            }
-        }
+    DisposableEffect(viewModel, state) {
+        viewModel.setTransitionState(state.observableTransitionState().map { it.toModel() })
+        onDispose { viewModel.setTransitionState(null) }
     }
-}
 
-/** Renders the given [ComposableScene]. */
-@Composable
-private fun Scene(
-    scene: ComposableScene,
-    onSceneChanged: (SceneModel) -> Unit,
-    modifier: Modifier = Modifier,
-) {
-    val destinationScenes: Map<UserAction, SceneModel> by scene.destinationScenes().collectAsState()
-    val swipeLeftDestinationScene = destinationScenes[UserAction.Swipe(Direction.LEFT)]
-    val swipeUpDestinationScene = destinationScenes[UserAction.Swipe(Direction.UP)]
-    val swipeRightDestinationScene = destinationScenes[UserAction.Swipe(Direction.RIGHT)]
-    val swipeDownDestinationScene = destinationScenes[UserAction.Swipe(Direction.DOWN)]
-    val backDestinationScene = destinationScenes[UserAction.Back]
-
-    // TODO(b/280880714): replace with the real UI and make sure to call onTransitionProgress.
-    Box(modifier) {
-        Column(
-            horizontalAlignment = Alignment.CenterHorizontally,
-            modifier = Modifier.align(Alignment.Center),
-        ) {
-            scene.Content(
-                modifier = Modifier,
-            )
-
-            Row(
-                horizontalArrangement = Arrangement.spacedBy(8.dp),
-            ) {
-                DirectionalButton(Direction.LEFT, swipeLeftDestinationScene, onSceneChanged)
-                DirectionalButton(Direction.UP, swipeUpDestinationScene, onSceneChanged)
-                DirectionalButton(Direction.RIGHT, swipeRightDestinationScene, onSceneChanged)
-                DirectionalButton(Direction.DOWN, swipeDownDestinationScene, onSceneChanged)
-            }
-
-            if (backDestinationScene != null) {
-                BackHandler { onSceneChanged.invoke(backDestinationScene) }
-            }
-        }
-    }
-}
-
-@Composable
-private fun DirectionalButton(
-    direction: Direction,
-    destinationScene: SceneModel?,
-    onSceneChanged: (SceneModel) -> Unit,
-    modifier: Modifier = Modifier,
-) {
-    Button(
-        onClick = { destinationScene?.let { onSceneChanged.invoke(it) } },
-        enabled = destinationScene != null,
-        modifier = modifier,
+    SceneTransitionLayout(
+        currentScene = currentSceneKey.toTransitionSceneKey(),
+        onChangeScene = { sceneKey -> viewModel.setCurrentScene(sceneKey.toModel()) },
+        transitions = transitions {},
+        state = state,
+        modifier = modifier.fillMaxSize(),
     ) {
-        Text(direction.name.lowercase(Locale.getDefault()))
+        sceneByKey.forEach { (sceneKey, composableScene) ->
+            scene(
+                key = sceneKey.toTransitionSceneKey(),
+                userActions =
+                    if (sceneKey == currentSceneKey) {
+                            currentDestinations
+                        } else {
+                            composableScene.destinationScenes().value
+                        }
+                        .map { (userAction, destinationSceneModel) ->
+                            toTransitionModels(userAction, destinationSceneModel)
+                        }
+                        .toMap(),
+            ) {
+                with(composableScene) {
+                    this@scene.Content(
+                        modifier = Modifier.fillMaxSize(),
+                    )
+                }
+            }
+        }
+    }
+}
+
+// TODO(b/293899074): remove this once we can use the one from SceneTransitionLayout.
+private fun SceneTransitionObservableTransitionState.toModel(): ObservableTransitionState {
+    return when (this) {
+        is SceneTransitionObservableTransitionState.Idle ->
+            ObservableTransitionState.Idle(scene.toModel().key)
+        is SceneTransitionObservableTransitionState.Transition ->
+            ObservableTransitionState.Transition(
+                fromScene = fromScene.toModel().key,
+                toScene = toScene.toModel().key,
+                progress = progress,
+            )
+    }
+}
+
+// TODO(b/293899074): remove this once we can use the one from SceneTransitionLayout.
+private fun toTransitionModels(
+    userAction: UserAction,
+    sceneModel: SceneModel,
+): Pair<SceneTransitionUserAction, SceneTransitionSceneKey> {
+    return userAction.toTransitionUserAction() to sceneModel.key.toTransitionSceneKey()
+}
+
+// TODO(b/293899074): remove this once we can use the one from SceneTransitionLayout.
+private fun SceneKey.toTransitionSceneKey(): SceneTransitionSceneKey {
+    return SceneTransitionSceneKey(
+        name = toString(),
+        identity = this,
+    )
+}
+
+// TODO(b/293899074): remove this once we can use the one from SceneTransitionLayout.
+private fun SceneTransitionSceneKey.toModel(): SceneModel {
+    return SceneModel(key = identity as SceneKey)
+}
+
+// TODO(b/293899074): remove this once we can use the one from SceneTransitionLayout.
+private fun UserAction.toTransitionUserAction(): SceneTransitionUserAction {
+    return when (this) {
+        is UserAction.Swipe ->
+            when (this.direction) {
+                Direction.LEFT -> Swipe.Left
+                Direction.UP -> Swipe.Up
+                Direction.RIGHT -> Swipe.Right
+                Direction.DOWN -> Swipe.Down
+            }
+        is UserAction.Back -> Back
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index b73e0b2..ff1cb5f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -26,6 +26,7 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.notifications.ui.composable.Notifications
@@ -63,7 +64,7 @@
             )
 
     @Composable
-    override fun Content(
+    override fun SceneScope.Content(
         modifier: Modifier,
     ) = ShadeScene(viewModel, modifier)
 
@@ -86,11 +87,12 @@
         horizontalAlignment = Alignment.CenterHorizontally,
         verticalArrangement = Arrangement.spacedBy(16.dp),
         modifier =
-            Modifier.fillMaxSize()
+            modifier
+                .fillMaxSize()
                 .clickable(onClick = { viewModel.onContentClicked() })
                 .padding(horizontal = 16.dp, vertical = 48.dp)
     ) {
-        QuickSettings(modifier = modifier.height(160.dp))
-        Notifications(modifier = modifier.weight(1f))
+        QuickSettings(modifier = Modifier.height(160.dp))
+        Notifications(modifier = Modifier.weight(1f))
     }
 }
diff --git a/packages/SystemUI/res/drawable/controls_list_divider_inset.xml b/packages/SystemUI/res-keyguard/drawable/controls_list_divider.xml
similarity index 65%
copy from packages/SystemUI/res/drawable/controls_list_divider_inset.xml
copy to packages/SystemUI/res-keyguard/drawable/controls_list_divider.xml
index ddfa18c..2fb7222 100644
--- a/packages/SystemUI/res/drawable/controls_list_divider_inset.xml
+++ b/packages/SystemUI/res-keyguard/drawable/controls_list_divider.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
+<!--
+     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.
@@ -13,8 +14,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<inset
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/controls_list_divider"
-    android:insetRight="@dimen/control_spinner_padding_horizontal"
-    android:insetLeft="@dimen/control_spinner_padding_horizontal" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/transparent" />
+    <size
+        android:width="@dimen/control_list_horizontal_spacing"
+        android:height="@dimen/control_list_vertical_spacing" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/controls_list_divider.xml b/packages/SystemUI/res/drawable/global_actions_list_divider.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/controls_list_divider.xml
rename to packages/SystemUI/res/drawable/global_actions_list_divider.xml
diff --git a/packages/SystemUI/res/drawable/controls_list_divider_inset.xml b/packages/SystemUI/res/drawable/global_actions_list_divider_inset.xml
similarity index 93%
rename from packages/SystemUI/res/drawable/controls_list_divider_inset.xml
rename to packages/SystemUI/res/drawable/global_actions_list_divider_inset.xml
index ddfa18c..170bc0d 100644
--- a/packages/SystemUI/res/drawable/controls_list_divider_inset.xml
+++ b/packages/SystemUI/res/drawable/global_actions_list_divider_inset.xml
@@ -15,6 +15,6 @@
 -->
 <inset
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/controls_list_divider"
+    android:drawable="@drawable/global_actions_list_divider"
     android:insetRight="@dimen/control_spinner_padding_horizontal"
     android:insetLeft="@dimen/control_spinner_padding_horizontal" />
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
index e1dbe69..f1939bb 100644
--- a/packages/SystemUI/res/layout/controls_base_item.xml
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -25,7 +25,6 @@
     android:focusable="true"
     android:screenReaderFocusable="true"
     android:stateListAnimator="@anim/control_state_list_animator"
-    android:layout_marginStart="@dimen/control_spacing"
     android:background="@drawable/control_background">
 
     <ImageView
diff --git a/packages/SystemUI/res/drawable/controls_list_divider_inset.xml b/packages/SystemUI/res/layout/controls_list_view.xml
similarity index 61%
copy from packages/SystemUI/res/drawable/controls_list_divider_inset.xml
copy to packages/SystemUI/res/layout/controls_list_view.xml
index ddfa18c..2831cbf 100644
--- a/packages/SystemUI/res/drawable/controls_list_divider_inset.xml
+++ b/packages/SystemUI/res/layout/controls_list_view.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
+<!--
+     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.
@@ -13,8 +14,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<inset
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/controls_list_divider"
-    android:insetRight="@dimen/control_spinner_padding_horizontal"
-    android:insetLeft="@dimen/control_spinner_padding_horizontal" />
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/controls_list"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:divider="@drawable/controls_list_divider"
+    android:orientation="vertical"
+    android:showDividers="middle" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_row.xml b/packages/SystemUI/res/layout/controls_row.xml
index 4cc461a..4923b05 100644
--- a/packages/SystemUI/res/layout/controls_row.xml
+++ b/packages/SystemUI/res/layout/controls_row.xml
@@ -14,9 +14,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="horizontal"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_marginBottom="@dimen/control_spacing" />
+    android:divider="@drawable/controls_list_divider"
+    android:orientation="horizontal"
+    android:showDividers="middle" />
diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml
index b1259e4..c60fb26 100644
--- a/packages/SystemUI/res/layout/controls_with_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_with_favorites.xml
@@ -20,7 +20,7 @@
 
     <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_height="@dimen/controls_header_menu_size"
         android:paddingHorizontal="@dimen/controls_header_horizontal_padding"
         android:layout_marginBottom="@dimen/controls_header_bottom_margin"
         android:orientation="horizontal">
@@ -85,10 +85,11 @@
         android:layout_weight="1"
         android:clipChildren="true"
         android:orientation="vertical"
-        android:paddingHorizontal="16dp"
+        android:padding="@dimen/controls_content_padding"
+        android:background="@drawable/controls_panel_background"
         android:scrollbars="none">
 
-        <include layout="@layout/global_actions_controls_list_view" />
+        <include layout="@layout/controls_list_view" />
 
     </ScrollView>
 
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
index 8bff1a1..6de10b4 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
@@ -14,34 +14,27 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
 -->
-<FrameLayout
+<com.android.systemui.shared.shadow.DoubleShadowTextClock
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="wrap_content"
-    android:layout_height="wrap_content">
-
-    <com.android.systemui.shared.shadow.DoubleShadowTextClock
-        android:id="@+id/time_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:fontFamily="@*android:string/config_clockFontFamily"
-        android:textColor="@android:color/white"
-        android:format12Hour="@string/dream_time_complication_12_hr_time_format"
-        android:format24Hour="@string/dream_time_complication_24_hr_time_format"
-        android:fontFeatureSettings="pnum, lnum"
-        android:includeFontPadding="false"
-        android:letterSpacing="0.02"
-        android:maxLines="1"
-        android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"
-        app:keyShadowBlur="@dimen/dream_overlay_clock_key_text_shadow_radius"
-        app:keyShadowOffsetX="@dimen/dream_overlay_clock_key_text_shadow_dx"
-        app:keyShadowOffsetY="@dimen/dream_overlay_clock_key_text_shadow_dy"
-        app:keyShadowAlpha="0.3"
-        app:ambientShadowBlur="@dimen/dream_overlay_clock_ambient_text_shadow_radius"
-        app:ambientShadowOffsetX="@dimen/dream_overlay_clock_ambient_text_shadow_dx"
-        app:ambientShadowOffsetY="@dimen/dream_overlay_clock_ambient_text_shadow_dy"
-        app:ambientShadowAlpha="0.3"
-        app:removeTextDescent="true"
-        app:textDescentExtraPadding="@dimen/dream_overlay_clock_text_descent_extra_padding" />
-
-</FrameLayout>
+    android:layout_height="wrap_content"
+    android:fontFamily="@*android:string/config_clockFontFamily"
+    android:textColor="@android:color/white"
+    android:format12Hour="@string/dream_time_complication_12_hr_time_format"
+    android:format24Hour="@string/dream_time_complication_24_hr_time_format"
+    android:fontFeatureSettings="pnum, lnum"
+    android:includeFontPadding="false"
+    android:letterSpacing="0.02"
+    android:maxLines="1"
+    android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"
+    app:keyShadowBlur="@dimen/dream_overlay_clock_key_text_shadow_radius"
+    app:keyShadowOffsetX="@dimen/dream_overlay_clock_key_text_shadow_dx"
+    app:keyShadowOffsetY="@dimen/dream_overlay_clock_key_text_shadow_dy"
+    app:keyShadowAlpha="0.3"
+    app:ambientShadowBlur="@dimen/dream_overlay_clock_ambient_text_shadow_radius"
+    app:ambientShadowOffsetX="@dimen/dream_overlay_clock_ambient_text_shadow_dx"
+    app:ambientShadowOffsetY="@dimen/dream_overlay_clock_ambient_text_shadow_dy"
+    app:ambientShadowAlpha="0.3"
+    app:removeTextDescent="true"
+    app:textDescentExtraPadding="@dimen/dream_overlay_clock_text_descent_extra_padding" />
diff --git a/packages/SystemUI/res/layout/global_actions_controls_list_view.xml b/packages/SystemUI/res/layout/global_actions_controls_list_view.xml
deleted file mode 100644
index e1c2611..0000000
--- a/packages/SystemUI/res/layout/global_actions_controls_list_view.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/global_actions_controls_list"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:layout_marginLeft="@dimen/global_actions_side_margin"
-    android:layout_marginRight="@dimen/global_actions_side_margin" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_actions_power_dialog.xml b/packages/SystemUI/res/layout/global_actions_power_dialog.xml
index ff3f0fb..3456515 100644
--- a/packages/SystemUI/res/layout/global_actions_power_dialog.xml
+++ b/packages/SystemUI/res/layout/global_actions_power_dialog.xml
@@ -18,7 +18,7 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:orientation="vertical"
-    android:divider="@drawable/controls_list_divider"
+    android:divider="@drawable/global_actions_list_divider"
     android:showDividers="middle"
 />
 
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 1681f7a..0667cd8 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -69,6 +69,9 @@
 
     <dimen name="controls_header_horizontal_padding">12dp</dimen>
     <dimen name="controls_content_margin_horizontal">16dp</dimen>
+    <dimen name="controls_content_padding">16dp</dimen>
+    <dimen name="control_list_vertical_spacing">8dp</dimen>
+    <dimen name="control_list_horizontal_spacing">16dp</dimen>
 
     <!-- Rear Display Education dimens -->
     <dimen name="rear_display_animation_width">246dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 2b1d9d6..7e892f7 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -103,4 +103,7 @@
 
     <dimen name="controls_header_horizontal_padding">12dp</dimen>
     <dimen name="controls_content_margin_horizontal">24dp</dimen>
+    <dimen name="controls_content_padding">24dp</dimen>
+    <dimen name="control_list_vertical_spacing">8dp</dimen>
+    <dimen name="control_list_horizontal_spacing">16dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index de913ac..d74eca6 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -24,6 +24,9 @@
 
     <dimen name="controls_header_horizontal_padding">28dp</dimen>
     <dimen name="controls_content_margin_horizontal">40dp</dimen>
+    <dimen name="controls_content_padding">32dp</dimen>
+    <dimen name="control_list_vertical_spacing">16dp</dimen>
+    <dimen name="control_list_horizontal_spacing">16dp</dimen>
 
     <dimen name="large_screen_shade_header_height">56dp</dimen>
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 9f4fc39..e942258 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -195,7 +195,7 @@
     <color name="control_primary_text">#E6FFFFFF</color>
     <color name="control_secondary_text">#99FFFFFF</color>
     <color name="control_default_foreground">@color/GM2_grey_500</color>
-    <color name="control_default_background">@color/GM2_grey_900</color>
+    <color name="control_default_background">#303134</color>
     <color name="control_spinner_dropdown">@*android:color/foreground_material_dark</color>
     <color name="control_more_vert">@*android:color/foreground_material_dark</color>
     <color name="control_enabled_light_background">#413C2D</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index de8287e..9bee972 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1238,6 +1238,7 @@
     <dimen name="controls_header_app_icon_size">24dp</dimen>
     <dimen name="controls_top_margin">48dp</dimen>
     <dimen name="controls_content_margin_horizontal">0dp</dimen>
+    <dimen name="controls_content_padding">16dp</dimen>
     <dimen name="control_header_text_size">24sp</dimen>
     <dimen name="control_item_text_size">14sp</dimen>
     <dimen name="control_menu_item_text_size">16sp</dimen>
@@ -1256,6 +1257,8 @@
     <dimen name="control_chevron_icon_size">20dp</dimen>
     <dimen name="control_spacing">8dp</dimen>
     <dimen name="control_list_divider">1dp</dimen>
+    <dimen name="control_list_vertical_spacing">8dp</dimen>
+    <dimen name="control_list_horizontal_spacing">12dp</dimen>
     <dimen name="control_corner_radius">14dp</dimen>
     <dimen name="control_height">104dp</dimen>
     <dimen name="control_padding">12dp</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 16eb21d..4d196aa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -421,6 +421,7 @@
      * Refresh clock. Called in response to TIME_TICK broadcasts.
      */
     void refresh() {
+        mLogBuffer.log(TAG, LogLevel.INFO, "refresh");
         if (mSmartspaceController != null) {
             mSmartspaceController.requestSmartspaceUpdate();
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index fe6bbc1..8e92941 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -340,6 +340,7 @@
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
         @Override
         public void onTimeChanged() {
+            Slog.v(TAG, "onTimeChanged");
             refreshTime();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
index e60d4e1..0c7d56f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
@@ -100,6 +100,9 @@
             )
         } else if (newState == STATE_ERROR && oldState != STATE_ERROR) {
             animateIconOnce(R.drawable.face_dialog_dark_to_error)
+            iconView.contentDescription = context.getString(
+                    R.string.keyguard_face_failed
+            )
         } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
             animateIconOnce(R.drawable.face_dialog_dark_to_checkmark)
             iconView.contentDescription = context.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index ebff0b0..39a45f7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -26,6 +26,7 @@
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
+import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -50,6 +51,7 @@
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.util.Log;
+import android.view.HapticFeedbackConstants;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
@@ -234,6 +236,8 @@
     public static final VibrationEffect EFFECT_CLICK =
             VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
 
+    public static final int LONG_PRESS = HapticFeedbackConstants.LONG_PRESS;
+
     private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
         @Override
         public void onScreenTurnedOn() {
@@ -926,12 +930,24 @@
     @VisibleForTesting
     public void playStartHaptic() {
         if (mAccessibilityManager.isTouchExplorationEnabled()) {
-            mVibrator.vibrate(
-                    Process.myUid(),
-                    mContext.getOpPackageName(),
-                    EFFECT_CLICK,
-                    "udfps-onStart-click",
-                    UDFPS_VIBRATION_ATTRIBUTES);
+            if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
+                if (mOverlay != null && mOverlay.getOverlayView() != null) {
+                    mVibrator.performHapticFeedback(
+                            mOverlay.getOverlayView(),
+                            HapticFeedbackConstants.CONTEXT_CLICK
+                    );
+                } else {
+                    Log.e(TAG, "No haptics played. Could not obtain overlay view to perform"
+                            + "vibration. Either the controller overlay is null or has no view");
+                }
+            } else {
+                mVibrator.vibrate(
+                        Process.myUid(),
+                        mContext.getOpPackageName(),
+                        EFFECT_CLICK,
+                        "udfps-onStart-click",
+                        UDFPS_VIBRATION_ATTRIBUTES);
+            }
         }
     }
 
@@ -1024,12 +1040,24 @@
             mKeyguardViewManager.showPrimaryBouncer(true);
 
             // play the same haptic as the LockIconViewController longpress
-            mVibrator.vibrate(
-                    Process.myUid(),
-                    mContext.getOpPackageName(),
-                    UdfpsController.EFFECT_CLICK,
-                    "aod-lock-icon-longpress",
-                    LOCK_ICON_VIBRATION_ATTRIBUTES);
+            if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
+                if (mOverlay != null && mOverlay.getOverlayView() != null) {
+                    mVibrator.performHapticFeedback(
+                            mOverlay.getOverlayView(),
+                            UdfpsController.LONG_PRESS
+                    );
+                } else {
+                    Log.e(TAG, "No haptics played. Could not obtain overlay view to perform"
+                            + "vibration. Either the controller overlay is null or has no view");
+                }
+            } else {
+                mVibrator.vibrate(
+                        Process.myUid(),
+                        mContext.getOpPackageName(),
+                        UdfpsController.EFFECT_CLICK,
+                        "aod-lock-icon-longpress",
+                        LOCK_ICON_VIBRATION_ATTRIBUTES);
+            }
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 9bbf1ef..b68b921 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -26,6 +26,7 @@
 import android.text.method.ScrollingMovementMethod
 import android.util.Log
 import android.view.View
+import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO
 import android.view.accessibility.AccessibilityManager
 import android.widget.Button
 import android.widget.TextView
@@ -335,6 +336,13 @@
                 // dismiss prompt when authenticated and confirmed
                 launch {
                     viewModel.isAuthenticated.collect { authState ->
+                        // Disable background view for cancelling authentication once authenticated,
+                        // and remove from talkback
+                        if (authState.isAuthenticated) {
+                            backgroundView.setOnClickListener(null)
+                            backgroundView.importantForAccessibility =
+                                IMPORTANT_FOR_ACCESSIBILITY_NO
+                        }
                         if (authState.isAuthenticatedAndConfirmed) {
                             view.announceForAccessibility(
                                 view.resources.getString(R.string.biometric_dialog_authenticated)
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamClockTimeComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamClockTimeComplication.java
index 9c3448b..dc32a59 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamClockTimeComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamClockTimeComplication.java
@@ -16,34 +16,36 @@
 
 package com.android.systemui.complication;
 
-import static com.android.systemui.complication.dagger.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_VIEW;
+import static com.android.systemui.complication.dagger.DreamClockTimeComplicationComponent.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_VIEW;
 import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS;
 
 import android.view.View;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.CoreStartable;
+import com.android.systemui.complication.dagger.DreamClockTimeComplicationComponent;
 import com.android.systemui.dagger.qualifiers.SystemUser;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.ViewController;
 import com.android.systemui.util.condition.ConditionalCoreStartable;
 
 import javax.inject.Inject;
 import javax.inject.Named;
-import javax.inject.Provider;
 
 /**
  * Clock Time Complication that produce Clock Time view holder.
  */
 public class DreamClockTimeComplication implements Complication {
-    private final Provider<DreamClockTimeViewHolder> mDreamClockTimeViewHolderProvider;
+    private final DreamClockTimeComplicationComponent.Factory mComponentFactory;
 
     /**
      * Default constructor for {@link DreamClockTimeComplication}.
      */
     @Inject
     public DreamClockTimeComplication(
-            Provider<DreamClockTimeViewHolder> dreamClockTimeViewHolderProvider) {
-        mDreamClockTimeViewHolderProvider = dreamClockTimeViewHolderProvider;
+            DreamClockTimeComplicationComponent.Factory componentFactory) {
+        mComponentFactory = componentFactory;
     }
 
     @Override
@@ -56,7 +58,7 @@
      */
     @Override
     public ViewHolder createView(ComplicationViewModel model) {
-        return mDreamClockTimeViewHolderProvider.get();
+        return mComponentFactory.create().getViewHolder();
     }
 
     /**
@@ -94,11 +96,14 @@
         private final ComplicationLayoutParams mLayoutParams;
 
         @Inject
-        DreamClockTimeViewHolder(@Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW) View view,
+        DreamClockTimeViewHolder(
+                @Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW) View view,
                 @Named(DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS)
-                        ComplicationLayoutParams layoutParams) {
+                        ComplicationLayoutParams layoutParams,
+                DreamClockTimeViewController viewController) {
             mView = view;
             mLayoutParams = layoutParams;
+            viewController.init();
         }
 
         @Override
@@ -111,4 +116,29 @@
             return mLayoutParams;
         }
     }
+
+    static class DreamClockTimeViewController extends ViewController<View> {
+        private final UiEventLogger mUiEventLogger;
+
+        @Inject
+        DreamClockTimeViewController(
+                @Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW) View view,
+                UiEventLogger uiEventLogger) {
+            super(view);
+
+            mUiEventLogger = uiEventLogger;
+        }
+
+        @Override
+        protected void onViewAttached() {
+            mView.setOnClickListener(this::onClick);
+        }
+
+        @Override
+        protected void onViewDetached() {}
+
+        private void onClick(View v) {
+            mUiEventLogger.log(DreamOverlayUiEvent.DREAM_CLOCK_TAPPED);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
index 4d99282..7ac1cc7 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
@@ -29,8 +29,6 @@
 import android.view.View;
 import android.widget.ImageView;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.CoreStartable;
 import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -201,23 +199,6 @@
 
         private final UiEventLogger mUiEventLogger;
 
-        @VisibleForTesting
-        public enum DreamOverlayEvent implements UiEventLogger.UiEventEnum {
-            @UiEvent(doc = "The home controls on the screensaver has been tapped.")
-            DREAM_HOME_CONTROLS_TAPPED(1212);
-
-            private final int mId;
-
-            DreamOverlayEvent(int id) {
-                mId = id;
-            }
-
-            @Override
-            public int getId() {
-                return mId;
-            }
-        }
-
         @Inject
         DreamHomeControlsChipViewController(
                 @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view,
@@ -246,7 +227,7 @@
         private void onClickHomeControls(View v) {
             if (DEBUG) Log.d(TAG, "home controls complication tapped");
 
-            mUiEventLogger.log(DreamOverlayEvent.DREAM_HOME_CONTROLS_TAPPED);
+            mUiEventLogger.log(DreamOverlayUiEvent.DREAM_HOME_CONTROLS_TAPPED);
 
             final Intent intent = new Intent(mContext, ControlsActivity.class)
                     .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK)
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamOverlayUiEvent.kt b/packages/SystemUI/src/com/android/systemui/complication/DreamOverlayUiEvent.kt
new file mode 100644
index 0000000..17cc829
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamOverlayUiEvent.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.complication
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger.UiEventEnum
+
+/** UI log events for the dream overlay. */
+enum class DreamOverlayUiEvent(private val mId: Int) : UiEventEnum {
+    @UiEvent(doc = "The home controls on the screensaver has been tapped.")
+    DREAM_HOME_CONTROLS_TAPPED(1212),
+    @UiEvent(doc = "The clock on the screensaver has been tapped") DREAM_CLOCK_TAPPED(1440),
+    @UiEvent(doc = "The weather on the screensaver has been tapped") DREAM_WEATHER_TAPPED(1441);
+
+    override fun getId(): Int {
+        return mId
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamClockTimeComplicationComponent.kt b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamClockTimeComplicationComponent.kt
new file mode 100644
index 0000000..87c3b2f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamClockTimeComplicationComponent.kt
@@ -0,0 +1,81 @@
+/*
+ *  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.complication.dagger
+
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.TextClock
+import com.android.internal.util.Preconditions
+import com.android.systemui.R
+import com.android.systemui.complication.DreamClockTimeComplication
+import com.android.systemui.complication.DreamClockTimeComplication.DreamClockTimeViewHolder
+import dagger.Module
+import dagger.Provides
+import dagger.Subcomponent
+import javax.inject.Named
+import javax.inject.Scope
+
+/** Responsible for generating dependencies for the [DreamClockTimeComplication]. */
+@Subcomponent(
+    modules = [DreamClockTimeComplicationComponent.DreamClockTimeComplicationModule::class]
+)
+@DreamClockTimeComplicationComponent.DreamClockTimeComplicationScope
+interface DreamClockTimeComplicationComponent {
+    /** Scope of the clock complication. */
+    @MustBeDocumented
+    @Retention(AnnotationRetention.RUNTIME)
+    @Scope
+    annotation class DreamClockTimeComplicationScope
+
+    /** Factory that generates a component for the clock complication. */
+    @Subcomponent.Factory
+    interface Factory {
+        fun create(): DreamClockTimeComplicationComponent
+    }
+
+    /** Creates a view holder for the clock complication. */
+    fun getViewHolder(): DreamClockTimeViewHolder
+
+    /** Module for providing injected values within the clock complication scope. */
+    @Module
+    interface DreamClockTimeComplicationModule {
+        companion object {
+            const val DREAM_CLOCK_TIME_COMPLICATION_VIEW = "clock_time_complication_view"
+            private const val TAG_WEIGHT = "'wght' "
+            private const val WEIGHT = 400
+
+            /** Provides the complication view. */
+            @Provides
+            @DreamClockTimeComplicationScope
+            @Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW)
+            fun provideComplicationView(layoutInflater: LayoutInflater): View {
+                val view =
+                    Preconditions.checkNotNull(
+                        layoutInflater.inflate(
+                            R.layout.dream_overlay_complication_clock_time,
+                            /* root = */ null,
+                            /* attachToRoot = */ false,
+                        ) as TextClock,
+                        "R.layout.dream_overlay_complication_clock_time did not properly inflate"
+                    )
+                view.setFontVariationSettings(TAG_WEIGHT + WEIGHT)
+                return view
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamClockTimeComplicationModule.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamClockTimeComplicationModule.java
deleted file mode 100644
index fd711ee..0000000
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamClockTimeComplicationModule.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2022 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.complication.dagger;
-
-
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.TextClock;
-
-import com.android.internal.util.Preconditions;
-import com.android.systemui.R;
-import com.android.systemui.complication.DreamClockTimeComplication;
-
-import dagger.Module;
-import dagger.Provides;
-
-import javax.inject.Named;
-
-/**
- * Module for providing {@link DreamClockTimeComplication}.
- */
-@Module
-public interface DreamClockTimeComplicationModule {
-    String DREAM_CLOCK_TIME_COMPLICATION_VIEW = "clock_time_complication_view";
-    String TAG_WEIGHT = "'wght' ";
-    int WEIGHT = 400;
-
-    /**
-     * Provides the complication view.
-     */
-    @Provides
-    @Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW)
-    static View provideComplicationView(LayoutInflater layoutInflater) {
-        final View view = Preconditions.checkNotNull(
-                        layoutInflater.inflate(R.layout.dream_overlay_complication_clock_time,
-                                null, false),
-                "R.layout.dream_overlay_complication_clock_time did not properly inflated");
-        ((TextClock) view.findViewById(R.id.time_view)).setFontVariationSettings(
-                TAG_WEIGHT + WEIGHT);
-        return view;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
index 98975fbd..776c972 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
@@ -35,10 +35,9 @@
  * Module for all components with corresponding dream layer complications registered in
  * {@link SystemUIBinder}.
  */
-@Module(includes = {
-                DreamClockTimeComplicationModule.class,
-        },
+@Module(
         subcomponents = {
+                DreamClockTimeComplicationComponent.class,
                 DreamHomeControlsComplicationComponent.class,
                 DreamMediaEntryComplicationComponent.class
         })
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index b387e4a..4c9dbe0 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -40,8 +40,6 @@
 import com.android.systemui.controls.controller.StructureInfo
 import com.android.systemui.controls.ui.ControlsActivity
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserTracker
 import java.util.concurrent.Executor
 import javax.inject.Inject
@@ -50,7 +48,6 @@
  * Activity for rearranging and removing controls for a given structure
  */
 open class ControlsEditingActivity @Inject constructor(
-    featureFlags: FeatureFlags,
     @Main private val mainExecutor: Executor,
     private val controller: ControlsControllerImpl,
     private val userTracker: UserTracker,
@@ -76,8 +73,6 @@
 
     private var isFromFavoriting: Boolean = false
 
-    private val isNewFlowEnabled: Boolean =
-        featureFlags.isEnabled(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS)
     private val userTrackerCallback: UserTracker.Callback = object : UserTracker.Callback {
         private val startingUser = controller.currentUserId
 
@@ -176,7 +171,7 @@
     private fun bindButtons() {
         addControls = requireViewById<Button>(R.id.addControls).apply {
             isEnabled = true
-            visibility = if (isNewFlowEnabled) View.VISIBLE else View.GONE
+            visibility = View.VISIBLE
             setOnClickListener {
                 if (saveButton.isEnabled) {
                     // The user has made changes
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 59fa7f5..23721c9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -33,7 +33,6 @@
 import android.widget.Button
 import android.widget.FrameLayout
 import android.widget.TextView
-import android.widget.Toast
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
 import androidx.activity.ComponentActivity
@@ -41,24 +40,19 @@
 import androidx.viewpager2.widget.ViewPager2
 import com.android.systemui.Prefs
 import com.android.systemui.R
-import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.TooltipManager
 import com.android.systemui.controls.controller.ControlsControllerImpl
 import com.android.systemui.controls.controller.StructureInfo
 import com.android.systemui.controls.ui.ControlsActivity
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserTracker
 import java.text.Collator
 import java.util.concurrent.Executor
 import javax.inject.Inject
 
 open class ControlsFavoritingActivity @Inject constructor(
-    featureFlags: FeatureFlags,
     @Main private val executor: Executor,
     private val controller: ControlsControllerImpl,
-    private val listingController: ControlsListingController,
     private val userTracker: UserTracker,
 ) : ComponentActivity() {
 
@@ -92,7 +86,6 @@
     private lateinit var pageIndicator: ManagementPageIndicator
     private var mTooltipManager: TooltipManager? = null
     private lateinit var doneButton: View
-    private lateinit var otherAppsButton: View
     private lateinit var rearrangeButton: Button
     private var listOfStructures = emptyList<StructureContainer>()
 
@@ -104,8 +97,6 @@
         get() = openSource == EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR
     private val fromEditing: Boolean
         get() = openSource == EXTRA_SOURCE_VALUE_FROM_EDITING
-    private val isNewFlowEnabled: Boolean =
-        featureFlags.isEnabled(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS)
     private val userTrackerCallback: UserTracker.Callback = object : UserTracker.Callback {
         private val startingUser = controller.currentUserId
 
@@ -124,20 +115,6 @@
         onBackPressed()
     }
 
-    private val listingCallback = object : ControlsListingController.ControlsListingCallback {
-
-        override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
-            if (serviceInfos.size > 1) {
-                val newVisibility = if (isNewFlowEnabled) View.GONE else View.VISIBLE
-                if (otherAppsButton.visibility != newVisibility) {
-                    otherAppsButton.post {
-                        otherAppsButton.visibility = newVisibility
-                    }
-                }
-            }
-        }
-    }
-
     override fun onBackPressed() {
         if (fromEditing) {
             animateExitAndFinish()
@@ -342,7 +319,7 @@
                 getString(R.string.controls_favorite_rearrange_button)
             }
             isEnabled = false
-            visibility = if (isNewFlowEnabled) View.VISIBLE else View.GONE
+            visibility = View.VISIBLE
             setOnClickListener {
                 if (component == null) return@setOnClickListener
                 saveFavorites()
@@ -361,24 +338,6 @@
                 )
             }
         }
-        otherAppsButton = requireViewById<Button>(R.id.other_apps).apply {
-            setOnClickListener {
-                if (doneButton.isEnabled) {
-                    // The user has made changes
-                    Toast.makeText(
-                            applicationContext,
-                            R.string.controls_favorite_toast_no_changes,
-                            Toast.LENGTH_SHORT
-                            ).show()
-                }
-                startActivity(
-                    Intent(context, ControlsProviderSelectorActivity::class.java),
-                    ActivityOptions
-                        .makeSceneTransitionAnimation(this@ControlsFavoritingActivity).toBundle()
-                )
-                animateExitAndFinish()
-            }
-        }
 
         doneButton = requireViewById<Button>(R.id.done).apply {
             isEnabled = false
@@ -415,7 +374,6 @@
     override fun onStart() {
         super.onStart()
 
-        listingController.addCallback(listingCallback)
         userTracker.addCallback(userTrackerCallback, executor)
 
         if (DEBUG) {
@@ -440,7 +398,6 @@
     override fun onStop() {
         super.onStop()
 
-        listingController.removeCallback(listingCallback)
         userTracker.removeCallback(userTrackerCallback)
 
         if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
index 5c2402b..4aef209 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
@@ -20,8 +20,6 @@
 import android.content.Context
 import android.content.SharedPreferences
 import com.android.systemui.R
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl
@@ -33,7 +31,6 @@
     private val context: Context,
     private val userFileManager: UserFileManager,
     private val userTracker: UserTracker,
-    private val featureFlags: FeatureFlags,
 ) : AuthorizedPanelsRepository {
 
     override fun getAuthorizedPanels(): Set<String> {
@@ -74,17 +71,8 @@
                 userTracker.userId,
             )
 
-        // We should add default packages in two cases:
-        // 1) We've never run this
-        // 2) APP_PANELS_REMOVE_APPS_ALLOWED got disabled after user removed all apps
-        val needToSetup =
-            if (featureFlags.isEnabled(Flags.APP_PANELS_REMOVE_APPS_ALLOWED)) {
-                sharedPref.getStringSet(KEY, null) == null
-            } else {
-                // There might be an empty set that need to be overridden after the feature has been
-                // turned off after being turned on
-                sharedPref.getStringSet(KEY, null).isNullOrEmpty()
-            }
+        // We should add default packages when we've never run this
+        val needToSetup = sharedPref.getStringSet(KEY, null) == null
         if (needToSetup) {
             sharedPref.edit().putStringSet(KEY, getPreferredPackages()).apply()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/SelectedComponentRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/SelectedComponentRepositoryImpl.kt
index 0fb5b66..c9edd4a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/panels/SelectedComponentRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/panels/SelectedComponentRepositoryImpl.kt
@@ -21,7 +21,6 @@
 import android.content.SharedPreferences
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl
@@ -83,11 +82,7 @@
     }
 
     override fun shouldAddDefaultComponent(): Boolean =
-        if (featureFlags.isEnabled(Flags.APP_PANELS_REMOVE_APPS_ALLOWED)) {
-            sharedPreferences.getBoolean(SHOULD_ADD_DEFAULT_PANEL, true)
-        } else {
-            true
-        }
+        sharedPreferences.getBoolean(SHOULD_ADD_DEFAULT_PANEL, true)
 
     override fun setShouldAddDefaultComponent(shouldAdd: Boolean) {
         sharedPreferences.edit().putBoolean(SHOULD_ADD_DEFAULT_PANEL, shouldAdd).apply()
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 631ed3c..b0491ce 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -72,7 +72,6 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -506,32 +505,22 @@
         val isPanel = selectedItem is SelectedItem.PanelItem
         val selectedStructure = (selectedItem as? SelectedItem.StructureItem)?.structure
                 ?: EMPTY_STRUCTURE
-        val newFlows = featureFlags.isEnabled(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS)
 
         val items = buildList {
             add(OverflowMenuAdapter.MenuItem(
                     context.getText(R.string.controls_open_app),
                     OPEN_APP_ID
             ))
-            if (newFlows || isPanel) {
-                if (extraApps) {
-                    add(OverflowMenuAdapter.MenuItem(
-                            context.getText(R.string.controls_menu_add_another_app),
-                            ADD_APP_ID
-                    ))
-                }
-                if (featureFlags.isEnabled(Flags.APP_PANELS_REMOVE_APPS_ALLOWED)) {
-                    add(OverflowMenuAdapter.MenuItem(
-                            context.getText(R.string.controls_menu_remove),
-                            REMOVE_APP_ID,
-                    ))
-                }
-            } else {
+            if (extraApps) {
                 add(OverflowMenuAdapter.MenuItem(
-                        context.getText(R.string.controls_menu_add),
-                        ADD_CONTROLS_ID
+                        context.getText(R.string.controls_menu_add_another_app),
+                        ADD_APP_ID
                 ))
             }
+            add(OverflowMenuAdapter.MenuItem(
+                    context.getText(R.string.controls_menu_remove),
+                    REMOVE_APP_ID,
+            ))
             if (!isPanel) {
                 add(OverflowMenuAdapter.MenuItem(
                         context.getText(R.string.controls_menu_edit),
@@ -665,7 +654,7 @@
 
         val maxColumns = ControlAdapter.findMaxColumns(activityContext.resources)
 
-        val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
+        val listView = parent.requireViewById(R.id.controls_list) as ViewGroup
         listView.removeAllViews()
         var lastRow: ViewGroup = createRow(inflater, listView)
         selectedStructure.controls.forEach {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 063a4d0..5a49475 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -270,7 +270,7 @@
 
     /** Migrate the lock icon view to the new keyguard root view. */
     // TODO(b/286552209): Tracking bug.
-    @JvmField val MIGRATE_LOCK_ICON = releasedFlag(240, "migrate_lock_icon")
+    @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon", teamfood = true)
 
     // TODO(b/288276738): Tracking bug.
     @JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag(241, "widget_on_keyguard")
@@ -617,7 +617,8 @@
     val CLIPBOARD_IMAGE_TIMEOUT = unreleasedFlag(1702, "clipboard_image_timeout", teamfood = true)
     // TODO(b/279405451): Tracking Bug
     @JvmField
-    val CLIPBOARD_SHARED_TRANSITIONS = unreleasedFlag(1703, "clipboard_shared_transitions")
+    val CLIPBOARD_SHARED_TRANSITIONS =
+            unreleasedFlag(1703, "clipboard_shared_transitions", teamfood = true)
 
     // TODO(b/283300105): Tracking Bug
     @JvmField val SCENE_CONTAINER = unreleasedFlag(1802, "scene_container")
@@ -628,14 +629,6 @@
     // 2000 - device controls
     @JvmField val APP_PANELS_ALL_APPS_ALLOWED = releasedFlag(2001, "app_panels_all_apps_allowed")
 
-    @JvmField
-    val CONTROLS_MANAGEMENT_NEW_FLOWS = releasedFlag(2002, "controls_management_new_flows")
-
-    // Enables removing app from Home control panel as a part of a new flow
-    // TODO(b/269132640): Tracking Bug
-    @JvmField
-    val APP_PANELS_REMOVE_APPS_ALLOWED = releasedFlag(2003, "app_panels_remove_apps_allowed")
-
     // 2200 - biometrics (udfps, sfps, BiometricPrompt, etc.)
     // TODO(b/259264861): Tracking Bug
     @JvmField val UDFPS_NEW_TOUCH_DETECTION = releasedFlag(2200, "udfps_new_touch_detection")
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
index de67ba8..6d083f6 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
@@ -87,7 +87,7 @@
         if (mIsDropDownMode) {
             // use a divider
             listView.setDividerHeight(res.getDimensionPixelSize(R.dimen.control_list_divider));
-            listView.setDivider(res.getDrawable(R.drawable.controls_list_divider_inset));
+            listView.setDivider(res.getDrawable(R.drawable.global_actions_list_divider_inset));
         } else {
             if (mAdapter == null) return;
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 5d7ea1c..e2929ae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -100,6 +100,7 @@
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
@@ -173,6 +174,8 @@
 import dagger.Lazy;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Objects;
@@ -257,6 +260,22 @@
     private static final int SYSTEM_READY = 18;
     private static final int CANCEL_KEYGUARD_EXIT_ANIM = 19;
 
+    /** Enum for reasons behind updating wakeAndUnlock state. */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            value = {
+                    WakeAndUnlockUpdateReason.HIDE,
+                    WakeAndUnlockUpdateReason.SHOW,
+                    WakeAndUnlockUpdateReason.FULFILL,
+                    WakeAndUnlockUpdateReason.WAKE_AND_UNLOCK,
+            })
+    @interface WakeAndUnlockUpdateReason {
+        int HIDE = 0;
+        int SHOW = 1;
+        int FULFILL = 2;
+        int WAKE_AND_UNLOCK = 3;
+    }
+
     /**
      * The default amount of time we stay awake (used for all key input)
      */
@@ -808,7 +827,7 @@
             // dreaming. It's time to wake up.
             if (mUnlockingAndWakingFromDream) {
                 Log.d(TAG, "waking from dream after unlock");
-                mUnlockingAndWakingFromDream = false;
+                setUnlockAndWakeFromDream(false, WakeAndUnlockUpdateReason.FULFILL);
 
                 if (mKeyguardStateController.isShowing()) {
                     Log.d(TAG, "keyguard showing after keyguardGone, dismiss");
@@ -2685,7 +2704,7 @@
 
             mKeyguardExitAnimationRunner = null;
             mWakeAndUnlocking = false;
-            mUnlockingAndWakingFromDream = false;
+            setUnlockAndWakeFromDream(false, WakeAndUnlockUpdateReason.SHOW);
             setPendingLock(false);
 
             // Force if we we're showing in the middle of hiding, to ensure we end up in the correct
@@ -2791,6 +2810,51 @@
         tryKeyguardDone();
     };
 
+    private void setUnlockAndWakeFromDream(boolean updatedValue,
+            @WakeAndUnlockUpdateReason int reason) {
+        if (updatedValue == mUnlockingAndWakingFromDream) {
+            return;
+        }
+
+        final String reasonDescription;
+
+        switch(reason) {
+            case WakeAndUnlockUpdateReason.FULFILL:
+                reasonDescription = "fulfilling existing request";
+                break;
+            case WakeAndUnlockUpdateReason.HIDE:
+                reasonDescription = "hiding keyguard";
+                break;
+            case WakeAndUnlockUpdateReason.SHOW:
+                reasonDescription = "showing keyguard";
+                break;
+            case WakeAndUnlockUpdateReason.WAKE_AND_UNLOCK:
+                reasonDescription = "waking to unlock";
+                break;
+            default:
+                throw new IllegalStateException("Unexpected value: " + reason);
+        }
+
+        final boolean unsetUnfulfilled = !updatedValue
+                && reason != WakeAndUnlockUpdateReason.FULFILL;
+
+        mUnlockingAndWakingFromDream = updatedValue;
+
+        final String description;
+
+        if (unsetUnfulfilled) {
+            description = "Interrupting request to wake and unlock";
+        } else if (mUnlockingAndWakingFromDream) {
+            description = "Initiating request to wake and unlock";
+        } else {
+            description = "Fulfilling request to wake and unlock";
+        }
+
+        Log.d(TAG, String.format(
+                "Updating waking and unlocking request to %b. description:[%s]. reason:[%s]",
+                mUnlockingAndWakingFromDream,  description, reasonDescription));
+    }
+
     /**
      * Handle message sent by {@link #hideLocked()}
      * @see #HIDE
@@ -2810,8 +2874,11 @@
 
             mHiding = true;
 
-            mUnlockingAndWakingFromDream = mStatusBarStateController.isDreaming()
-                    && !mStatusBarStateController.isDozing();
+            // If waking and unlocking, waking from dream has been set properly.
+            if (!mWakeAndUnlocking) {
+                setUnlockAndWakeFromDream(mStatusBarStateController.isDreaming()
+                        && mPM.isInteractive(), WakeAndUnlockUpdateReason.HIDE);
+            }
 
             if ((mShowing && !mOccluded) || mUnlockingAndWakingFromDream) {
                 if (mUnlockingAndWakingFromDream) {
@@ -3218,7 +3285,8 @@
                 flags |= StatusBarManager.DISABLE_RECENT;
             }
 
-            if (mPowerGestureIntercepted && mOccluded && isSecure()) {
+            if (mPowerGestureIntercepted && mOccluded && isSecure()
+                    && mUpdateMonitor.isFaceEnrolled()) {
                 flags |= StatusBarManager.DISABLE_RECENT;
             }
 
@@ -3313,9 +3381,14 @@
         }
     }
 
-    public void onWakeAndUnlocking() {
+    /**
+     * Informs the keyguard view mediator that the device is waking and unlocking.
+     * @param fromDream Whether waking and unlocking is happening over an interactive dream.
+     */
+    public void onWakeAndUnlocking(boolean fromDream) {
         Trace.beginSection("KeyguardViewMediator#onWakeAndUnlocking");
         mWakeAndUnlocking = true;
+        setUnlockAndWakeFromDream(fromDream, WakeAndUnlockUpdateReason.WAKE_AND_UNLOCK);
 
         mKeyguardViewControllerLazy.get().notifyKeyguardAuthenticated(/* primaryAuth */ false);
         userActivity();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 2085c87..888f746 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -24,9 +24,11 @@
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.util.kotlin.Utils.Companion.toTriple
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
 @SysUISingleton
@@ -43,19 +45,27 @@
     ) {
 
     override fun start() {
-        listenForAodToLockscreen()
+        listenForAodToLockscreenOrOccluded()
         listenForAodToGone()
     }
 
-    private fun listenForAodToLockscreen() {
+    private fun listenForAodToLockscreenOrOccluded() {
         scope.launch {
             keyguardInteractor
                 .dozeTransitionTo(DozeStateModel.FINISH)
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
-                .collect { pair ->
-                    val (dozeToAod, lastStartedStep) = pair
+                .sample(
+                    combine(
+                        transitionInteractor.startedKeyguardTransitionStep,
+                        keyguardInteractor.isKeyguardOccluded,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect { (_, lastStartedStep, occluded) ->
                     if (lastStartedStep.to == KeyguardState.AOD) {
-                        startTransitionTo(KeyguardState.LOCKSCREEN)
+                        startTransitionTo(
+                            if (occluded) KeyguardState.OCCLUDED else KeyguardState.LOCKSCREEN
+                        )
                     }
                 }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index c867c43..76d9893 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -23,10 +23,12 @@
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.util.kotlin.Utils.Companion.toTriple
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
 @SysUISingleton
@@ -43,20 +45,29 @@
     ) {
 
     override fun start() {
-        listenForDozingToLockscreen()
+        listenForDozingToLockscreenOrOccluded()
         listenForDozingToGone()
     }
 
-    private fun listenForDozingToLockscreen() {
+    private fun listenForDozingToLockscreenOrOccluded() {
         scope.launch {
             keyguardInteractor.wakefulnessModel
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
-                .collect { (wakefulnessModel, lastStartedTransition) ->
+                .sample(
+                    combine(
+                        transitionInteractor.startedKeyguardTransitionStep,
+                        keyguardInteractor.isKeyguardOccluded,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect { (wakefulnessModel, lastStartedTransition, occluded) ->
                     if (
                         wakefulnessModel.isStartingToWakeOrAwake() &&
                             lastStartedTransition.to == KeyguardState.DOZING
                     ) {
-                        startTransitionTo(KeyguardState.LOCKSCREEN)
+                        startTransitionTo(
+                            if (occluded) KeyguardState.OCCLUDED else KeyguardState.LOCKSCREEN
+                        )
                     }
                 }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
new file mode 100644
index 0000000..dac6ef5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
@@ -0,0 +1,169 @@
+/*
+ * 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.animation.ValueAnimator
+import com.android.app.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.util.kotlin.Utils.Companion.toTriple
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class FromDreamingLockscreenHostedTransitionInteractor
+@Inject
+constructor(
+    override val transitionRepository: KeyguardTransitionRepository,
+    override val transitionInteractor: KeyguardTransitionInteractor,
+    @Application private val scope: CoroutineScope,
+    private val keyguardInteractor: KeyguardInteractor,
+) :
+    TransitionInteractor(
+        fromState = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+    ) {
+
+    override fun start() {
+        listenForDreamingLockscreenHostedToLockscreen()
+        listenForDreamingLockscreenHostedToGone()
+        listenForDreamingLockscreenHostedToDozing()
+        listenForDreamingLockscreenHostedToOccluded()
+        listenForDreamingLockscreenHostedToPrimaryBouncer()
+    }
+
+    private fun listenForDreamingLockscreenHostedToLockscreen() {
+        scope.launch {
+            keyguardInteractor.isActiveDreamLockscreenHosted
+                // Add a slight delay to prevent transitioning to lockscreen from happening too soon
+                // as dozing can arrive in a slight gap after the lockscreen hosted dream stops.
+                .onEach { delay(50) }
+                .sample(
+                    combine(
+                        keyguardInteractor.dozeTransitionModel,
+                        transitionInteractor.startedKeyguardTransitionStep,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect {
+                    (isActiveDreamLockscreenHosted, dozeTransitionModel, lastStartedTransition) ->
+                    if (
+                        !isActiveDreamLockscreenHosted &&
+                            DozeStateModel.isDozeOff(dozeTransitionModel.to) &&
+                            lastStartedTransition.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+                    ) {
+                        startTransitionTo(KeyguardState.LOCKSCREEN)
+                    }
+                }
+        }
+    }
+
+    private fun listenForDreamingLockscreenHostedToOccluded() {
+        scope.launch {
+            keyguardInteractor.isActiveDreamLockscreenHosted
+                .sample(
+                    combine(
+                        keyguardInteractor.isKeyguardOccluded,
+                        transitionInteractor.startedKeyguardTransitionStep,
+                        ::Pair,
+                    ),
+                    ::toTriple
+                )
+                .collect { (isActiveDreamLockscreenHosted, isOccluded, lastStartedTransition) ->
+                    if (
+                        isOccluded &&
+                            !isActiveDreamLockscreenHosted &&
+                            lastStartedTransition.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+                    ) {
+                        startTransitionTo(KeyguardState.OCCLUDED)
+                    }
+                }
+        }
+    }
+
+    private fun listenForDreamingLockscreenHostedToPrimaryBouncer() {
+        scope.launch {
+            keyguardInteractor.primaryBouncerShowing
+                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { (isBouncerShowing, lastStartedTransitionStep) ->
+                    if (
+                        isBouncerShowing &&
+                            lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+                    ) {
+                        startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
+                    }
+                }
+        }
+    }
+
+    private fun listenForDreamingLockscreenHostedToGone() {
+        scope.launch {
+            keyguardInteractor.biometricUnlockState
+                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { (biometricUnlockState, lastStartedTransitionStep) ->
+                    if (
+                        lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED &&
+                            biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+                    ) {
+                        startTransitionTo(KeyguardState.GONE)
+                    }
+                }
+        }
+    }
+
+    private fun listenForDreamingLockscreenHostedToDozing() {
+        scope.launch {
+            combine(
+                    keyguardInteractor.dozeTransitionModel,
+                    transitionInteractor.startedKeyguardTransitionStep,
+                    ::Pair
+                )
+                .collect { (dozeTransitionModel, lastStartedTransitionStep) ->
+                    if (
+                        dozeTransitionModel.to == DozeStateModel.DOZE &&
+                            lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+                    ) {
+                        startTransitionTo(KeyguardState.DOZING)
+                    }
+                }
+        }
+    }
+
+    override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
+        return ValueAnimator().apply {
+            interpolator = Interpolators.LINEAR
+            duration =
+                if (toState == KeyguardState.LOCKSCREEN) TO_LOCKSCREEN_DURATION.inWholeMilliseconds
+                else DEFAULT_DURATION.inWholeMilliseconds
+        }
+    }
+
+    companion object {
+        private val DEFAULT_DURATION = 500.milliseconds
+        val TO_LOCKSCREEN_DURATION = 1167.milliseconds
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 98d7434..954ff6f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -94,11 +94,16 @@
 
     private fun listenForDreamingToGone() {
         scope.launch {
-            keyguardInteractor.biometricUnlockState.collect { biometricUnlockState ->
-                if (biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM) {
-                    startTransitionTo(KeyguardState.GONE)
+            keyguardInteractor.biometricUnlockState
+                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { (biometricUnlockState, lastStartedTransitionStep) ->
+                    if (
+                        lastStartedTransitionStep.to == KeyguardState.DREAMING &&
+                            biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+                    ) {
+                        startTransitionTo(KeyguardState.GONE)
+                    }
                 }
-            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index f82633f..2b08b3d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -48,6 +48,7 @@
         listenForGoneToAodOrDozing()
         listenForGoneToDreaming()
         listenForGoneToLockscreen()
+        listenForGoneToDreamingLockscreenHosted()
     }
 
     // Primarily for when the user chooses to lock down the device
@@ -63,12 +64,35 @@
         }
     }
 
+    private fun listenForGoneToDreamingLockscreenHosted() {
+        scope.launch {
+            keyguardInteractor.isActiveDreamLockscreenHosted
+                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { (isActiveDreamLockscreenHosted, lastStartedStep) ->
+                    if (isActiveDreamLockscreenHosted && lastStartedStep.to == KeyguardState.GONE) {
+                        startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+                    }
+                }
+        }
+    }
+
     private fun listenForGoneToDreaming() {
         scope.launch {
             keyguardInteractor.isAbleToDream
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
-                .collect { (isAbleToDream, lastStartedStep) ->
-                    if (isAbleToDream && lastStartedStep.to == KeyguardState.GONE) {
+                .sample(
+                    combine(
+                        transitionInteractor.startedKeyguardTransitionStep,
+                        keyguardInteractor.isActiveDreamLockscreenHosted,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect { (isAbleToDream, lastStartedStep, isActiveDreamLockscreenHosted) ->
+                    if (
+                        isAbleToDream &&
+                            lastStartedStep.to == KeyguardState.GONE &&
+                            !isActiveDreamLockscreenHosted
+                    ) {
                         startTransitionTo(KeyguardState.DREAMING)
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index ed1bf3e..6b28b27 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -70,17 +70,27 @@
                     combine(
                         transitionInteractor.startedKeyguardTransitionStep,
                         transitionInteractor.finishedKeyguardState,
-                        ::Pair
+                        keyguardInteractor.isActiveDreamLockscreenHosted,
+                        ::Triple
                     ),
-                    ::toTriple
+                    ::toQuad
                 )
-                .collect { (isAbleToDream, lastStartedTransition, finishedKeyguardState) ->
+                .collect {
+                    (
+                        isAbleToDream,
+                        lastStartedTransition,
+                        finishedKeyguardState,
+                        isActiveDreamLockscreenHosted) ->
                     val isOnLockscreen = finishedKeyguardState == KeyguardState.LOCKSCREEN
                     val isTransitionInterruptible =
                         lastStartedTransition.to == KeyguardState.LOCKSCREEN &&
                             !invalidFromStates.contains(lastStartedTransition.from)
                     if (isAbleToDream && (isOnLockscreen || isTransitionInterruptible)) {
-                        startTransitionTo(KeyguardState.DREAMING)
+                        if (isActiveDreamLockscreenHosted) {
+                            startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+                        } else {
+                            startTransitionTo(KeyguardState.DREAMING)
+                        }
                     }
                 }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index e1754f5..9142d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.WakefulnessState
 import com.android.systemui.util.kotlin.Utils.Companion.toQuad
+import com.android.systemui.util.kotlin.Utils.Companion.toQuint
+import com.android.systemui.util.kotlin.Utils.Companion.toTriple
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -52,6 +54,7 @@
         listenForPrimaryBouncerToGone()
         listenForPrimaryBouncerToAodOrDozing()
         listenForPrimaryBouncerToLockscreenOrOccluded()
+        listenForPrimaryBouncerToDreamingLockscreenHosted()
     }
 
     private fun listenForPrimaryBouncerToLockscreenOrOccluded() {
@@ -62,17 +65,24 @@
                         keyguardInteractor.wakefulnessModel,
                         transitionInteractor.startedKeyguardTransitionStep,
                         keyguardInteractor.isKeyguardOccluded,
-                        ::Triple
+                        keyguardInteractor.isActiveDreamLockscreenHosted,
+                        ::toQuad
                     ),
-                    ::toQuad
+                    ::toQuint
                 )
-                .collect { (isBouncerShowing, wakefulnessState, lastStartedTransitionStep, occluded)
-                    ->
+                .collect {
+                    (
+                        isBouncerShowing,
+                        wakefulnessState,
+                        lastStartedTransitionStep,
+                        occluded,
+                        isActiveDreamLockscreenHosted) ->
                     if (
                         !isBouncerShowing &&
                             lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER &&
                             (wakefulnessState.state == WakefulnessState.AWAKE ||
-                                wakefulnessState.state == WakefulnessState.STARTING_TO_WAKE)
+                                wakefulnessState.state == WakefulnessState.STARTING_TO_WAKE) &&
+                            !isActiveDreamLockscreenHosted
                     ) {
                         startTransitionTo(
                             if (occluded) KeyguardState.OCCLUDED else KeyguardState.LOCKSCREEN
@@ -111,6 +121,30 @@
         }
     }
 
+    private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
+        scope.launch {
+            keyguardInteractor.primaryBouncerShowing
+                .sample(
+                    combine(
+                        keyguardInteractor.isActiveDreamLockscreenHosted,
+                        transitionInteractor.startedKeyguardTransitionStep,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect {
+                    (isBouncerShowing, isActiveDreamLockscreenHosted, lastStartedTransitionStep) ->
+                    if (
+                        !isBouncerShowing &&
+                            isActiveDreamLockscreenHosted &&
+                            lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
+                    ) {
+                        startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+                    }
+                }
+        }
+    }
+
     private fun listenForPrimaryBouncerToGone() {
         scope.launch {
             keyguardInteractor.isKeyguardGoingAway
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 864c4db..53d3c07 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -37,7 +37,6 @@
 import com.android.systemui.keyguard.shared.model.ScreenModel
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.WakefulnessModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
@@ -101,7 +100,7 @@
     /** Whether the system is dreaming with an overlay active */
     val isDreamingWithOverlay: Flow<Boolean> = repository.isDreamingWithOverlay
     /** Whether the system is dreaming and the active dream is hosted in lockscreen */
-    val isActiveDreamLockscreenHosted: Flow<Boolean> = repository.isActiveDreamLockscreenHosted
+    val isActiveDreamLockscreenHosted: StateFlow<Boolean> = repository.isActiveDreamLockscreenHosted
     /** Event for when the camera gesture is detected */
     val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> = conflatedCallbackFlow {
         val callback =
@@ -241,8 +240,16 @@
         repository.setQuickSettingsVisible(isVisible)
     }
 
-    fun setKeyguardRootVisibility(statusBarState: Int, goingToFullShade: Boolean, isOcclusionTransitionRunning: Boolean) {
-        repository.setKeyguardVisibility(statusBarState, goingToFullShade, isOcclusionTransitionRunning)
+    fun setKeyguardRootVisibility(
+        statusBarState: Int,
+        goingToFullShade: Boolean,
+        isOcclusionTransitionRunning: Boolean
+    ) {
+        repository.setKeyguardVisibility(
+            statusBarState,
+            goingToFullShade,
+            isOcclusionTransitionRunning
+        )
     }
 
     fun setClockPosition(x: Int, y: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index efc1bd0..ba7b987 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -45,6 +45,7 @@
                     is FromOccludedTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromDozingTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromAlternateBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
+                    is FromDreamingLockscreenHostedTransitionInteractor -> Log.d(TAG, "Started $it")
                 }
             it.start()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 45bf20d..8c4c7ae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
@@ -89,12 +90,20 @@
     val dreamingToLockscreenTransition: Flow<TransitionStep> =
         repository.transition(DREAMING, LOCKSCREEN)
 
+    /** DREAMING_LOCKSCREEN_HOSTED->LOCKSCREEN transition information. */
+    val dreamingLockscreenHostedToLockscreenTransition: Flow<TransitionStep> =
+        repository.transition(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN)
+
     /** GONE->AOD transition information. */
     val goneToAodTransition: Flow<TransitionStep> = repository.transition(GONE, AOD)
 
     /** GONE->DREAMING transition information. */
     val goneToDreamingTransition: Flow<TransitionStep> = repository.transition(GONE, DREAMING)
 
+    /** GONE->DREAMING_LOCKSCREEN_HOSTED transition information. */
+    val goneToDreamingLockscreenHostedTransition: Flow<TransitionStep> =
+        repository.transition(GONE, DREAMING_LOCKSCREEN_HOSTED)
+
     /** LOCKSCREEN->AOD transition information. */
     val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
 
@@ -102,6 +111,10 @@
     val lockscreenToDreamingTransition: Flow<TransitionStep> =
         repository.transition(LOCKSCREEN, DREAMING)
 
+    /** LOCKSCREEN->DREAMING_LOCKSCREEN_HOSTED transition information. */
+    val lockscreenToDreamingLockscreenHostedTransition: Flow<TransitionStep> =
+        repository.transition(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED)
+
     /** LOCKSCREEN->OCCLUDED transition information. */
     val lockscreenToOccludedTransition: Flow<TransitionStep> =
         repository.transition(LOCKSCREEN, OCCLUDED)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 4244e55..6115d90 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -92,6 +92,7 @@
                 KeyguardState.DOZING -> false
                 KeyguardState.AOD -> false
                 KeyguardState.DREAMING -> true
+                KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true
                 KeyguardState.ALTERNATE_BOUNCER -> true
                 KeyguardState.PRIMARY_BOUNCER -> true
                 KeyguardState.LOCKSCREEN -> true
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
index d9690b7..56f5529 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
@@ -50,6 +50,12 @@
 
     @Binds
     @IntoSet
+    abstract fun fromDreamingLockscreenHosted(
+        impl: FromDreamingLockscreenHostedTransitionInteractor
+    ): TransitionInteractor
+
+    @Binds
+    @IntoSet
     abstract fun fromOccluded(impl: FromOccludedTransitionInteractor): TransitionInteractor
 
     @Binds
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 87b4321..1e20cdb 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
@@ -35,6 +35,12 @@
      * parties to present their own UI over keyguard, like a screensaver.
      */
     DREAMING,
+    /*
+     * A device state after the device times out, which can be from both LOCKSCREEN or GONE states.
+     * It is a special version of DREAMING state but not DOZING. The active dream will be windowless
+     * and hosted in the lockscreen.
+     */
+    DREAMING_LOCKSCREEN_HOSTED,
     /**
      * The device has entered a special low-power mode within SystemUI, also called the Always-on
      * Display (AOD). A minimal UI is presented to show critical information. If the device is in
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
new file mode 100644
index 0000000..113f01c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down GONE->DREAMING_LOCKSCREEN_HOSTED transition into discrete steps for corresponding
+ * views to consume.
+ */
+@SysUISingleton
+class GoneToDreamingLockscreenHostedTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+) {
+
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = TO_DREAMING_DURATION,
+            transitionFlow = interactor.goneToDreamingLockscreenHostedTransition,
+        )
+
+    /** Lockscreen views alpha - hide immediately */
+    val lockscreenAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 1.milliseconds,
+            onStep = { 0f },
+        )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index 44e1fd1..cca96b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -66,6 +66,23 @@
             },
         )
 
+    /** Lockscreen alpha */
+    val lockscreenAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 50.milliseconds,
+            onStart = {
+                leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide()
+                willRunDismissFromKeyguard = primaryBouncerInteractor.willRunDismissFromKeyguard()
+            },
+            onStep = {
+                if (willRunDismissFromKeyguard || leaveShadeOpen) {
+                    1f
+                } else {
+                    0f
+                }
+            },
+        )
+
     /** Scrim alpha values */
     val scrimAlpha: Flow<ScrimAlpha> =
         transitionAnimation
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
index b307f1b..dd58607 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
@@ -112,6 +112,7 @@
             KeyguardState.OFF,
             KeyguardState.DOZING,
             KeyguardState.DREAMING,
+            KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
             KeyguardState.AOD,
             KeyguardState.PRIMARY_BOUNCER,
             KeyguardState.GONE,
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 2adc211..0842fe0 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -31,6 +31,7 @@
 import android.content.pm.PackageManager
 import android.content.pm.ShortcutManager
 import android.graphics.drawable.Icon
+import android.os.Process
 import android.os.UserHandle
 import android.os.UserManager
 import android.provider.Settings
@@ -317,7 +318,9 @@
             return
         }
 
-        if (user == userTracker.userHandle) {
+        // When switched to a secondary user, the sysUI is still running in the main user, we will
+        // need to update the shortcut in the secondary user.
+        if (user == getCurrentRunningUser()) {
             updateNoteTaskAsUserInternal(user)
         } else {
             // TODO(b/278729185): Replace fire and forget service with a bounded service.
@@ -354,6 +357,9 @@
         updateNoteTaskAsUser(user)
     }
 
+    // Returns the [UserHandle] that this class is running on.
+    @VisibleForTesting internal fun getCurrentRunningUser(): UserHandle = Process.myUserHandle()
+
     private val SecureSettings.preferredUser: UserHandle
         get() {
             val trackingUserId = userTracker.userHandle.identifier
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 3e7bdd1..9bb192b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -34,6 +34,7 @@
 import android.service.notification.ZenModeConfig;
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.View;
 import android.widget.Switch;
 
@@ -315,6 +316,7 @@
 
     private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
         public void onZenChanged(int zen) {
+            Log.d(TAG, "Zen changed to " + zen + ". Requesting refresh of tile.");
             refreshState(zen);
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
index 1fca488..fee3960 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
@@ -14,16 +14,23 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
 package com.android.systemui.scene.data.repository
 
+import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.scene.shared.model.SceneTransitionModel
 import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
 
 /** Source of truth for scene framework application state. */
 class SceneContainerRepository
@@ -38,8 +45,17 @@
     private val _currentScene = MutableStateFlow(SceneModel(config.initialSceneKey))
     val currentScene: StateFlow<SceneModel> = _currentScene.asStateFlow()
 
-    private val _transitionProgress = MutableStateFlow(1f)
-    val transitionProgress: StateFlow<Float> = _transitionProgress.asStateFlow()
+    private val transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null)
+    val transitionProgress: Flow<Float> =
+        transitionState.flatMapLatest { observableTransitionStateFlow ->
+            observableTransitionStateFlow?.flatMapLatest { observableTransitionState ->
+                when (observableTransitionState) {
+                    is ObservableTransitionState.Idle -> flowOf(1f)
+                    is ObservableTransitionState.Transition -> observableTransitionState.progress
+                }
+            }
+                ?: flowOf(1f)
+        }
 
     private val _transitions = MutableStateFlow<SceneTransitionModel?>(null)
     val transitions: StateFlow<SceneTransitionModel?> = _transitions.asStateFlow()
@@ -92,8 +108,12 @@
         _isVisible.value = isVisible
     }
 
-    /** Sets scene transition progress to the current scene in the container with the given name. */
-    fun setSceneTransitionProgress(progress: Float) {
-        _transitionProgress.value = progress
+    /**
+     * Binds the given flow so the system remembers it.
+     *
+     * Note that you must call is with `null` when the UI is done or risk a memory leak.
+     */
+    fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
+        this.transitionState.value = transitionState
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 39daad3..b09a5cf 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -18,11 +18,13 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.scene.data.repository.SceneContainerRepository
+import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.RemoteUserInput
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.scene.shared.model.SceneTransitionModel
 import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -69,13 +71,17 @@
     /** Whether the container with the given name is visible. */
     val isVisible: StateFlow<Boolean> = repository.isVisible
 
-    /** Sets scene transition progress to the current scene in the container with the given name. */
-    fun setSceneTransitionProgress(progress: Float) {
-        repository.setSceneTransitionProgress(progress)
+    /**
+     * Binds the given flow so the system remembers it.
+     *
+     * Note that you must call is with `null` when the UI is done or risk a memory leak.
+     */
+    fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
+        repository.setTransitionState(transitionState)
     }
 
     /** Progress of the transition into the current scene in the container with the given name. */
-    val transitionProgress: StateFlow<Float> = repository.transitionProgress
+    val transitionProgress: Flow<Float> = repository.transitionProgress
 
     /**
      * Scene transitions as pairs of keys. A new value is emitted exactly once, each time a scene
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
new file mode 100644
index 0000000..9a30aa6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 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.scene.shared.model
+
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * This is a fork of a class by the same name in the `com.android.compose.animation.scene` package.
+ *
+ * TODO(b/293899074): remove this fork, once we can compile Compose into System UI.
+ */
+sealed class ObservableTransitionState {
+    /** No transition/animation is currently running. */
+    data class Idle(val scene: SceneKey) : ObservableTransitionState()
+
+    /** There is a transition animating between two scenes. */
+    data class Transition(
+        val fromScene: SceneKey,
+        val toScene: SceneKey,
+        val progress: Flow<Float>,
+    ) : ObservableTransitionState()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index f44748a..bd73e36 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -19,10 +19,12 @@
 import android.view.MotionEvent
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.RemoteUserInput
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 
 /** Models UI state for the scene container. */
@@ -54,9 +56,13 @@
         interactor.setCurrentScene(scene)
     }
 
-    /** Notifies of the progress of a scene transition. */
-    fun setSceneTransitionProgress(progress: Float) {
-        interactor.setSceneTransitionProgress(progress)
+    /**
+     * Binds the given flow so the system remembers it.
+     *
+     * Note that you must call is with `null` when the UI is done or risk a memory leak.
+     */
+    fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
+        interactor.setTransitionState(transitionState)
     }
 
     /** Handles a [MotionEvent] representing remote user input. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 2192b436..416f147 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -140,12 +140,14 @@
 import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
 import com.android.systemui.keyguard.ui.view.KeyguardRootView;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
 import com.android.systemui.media.controls.ui.KeyguardMediaController;
 import com.android.systemui.media.controls.ui.MediaHierarchyManager;
@@ -597,7 +599,11 @@
     private final OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
     private final LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
     private final GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
+    private final GoneToDreamingLockscreenHostedTransitionViewModel
+            mGoneToDreamingLockscreenHostedTransitionViewModel;
+
     private final LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
+    private final PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
 
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     private final KeyguardInteractor mKeyguardInteractor;
@@ -605,6 +611,7 @@
     private final CoroutineDispatcher mMainDispatcher;
     private boolean mIsAnyMultiShadeExpanded;
     private boolean mIsOcclusionTransitionRunning = false;
+    private boolean mIsGoneToDreamingLockscreenHostedTransitionRunning;
     private int mDreamingToLockscreenTransitionTranslationY;
     private int mOccludedToLockscreenTransitionTranslationY;
     private int mLockscreenToDreamingTransitionTranslationY;
@@ -652,6 +659,25 @@
                     step.getTransitionState() == TransitionState.RUNNING;
             };
 
+    private final Consumer<TransitionStep> mGoneToDreamingLockscreenHostedTransition =
+            (TransitionStep step) -> {
+                mIsOcclusionTransitionRunning =
+                        step.getTransitionState() == TransitionState.RUNNING;
+                mIsGoneToDreamingLockscreenHostedTransitionRunning = mIsOcclusionTransitionRunning;
+            };
+
+    private final Consumer<TransitionStep> mLockscreenToDreamingLockscreenHostedTransition =
+            (TransitionStep step) -> {
+                mIsOcclusionTransitionRunning =
+                        step.getTransitionState() == TransitionState.RUNNING;
+            };
+
+    private final Consumer<TransitionStep> mDreamingLockscreenHostedToLockscreenTransition =
+            (TransitionStep step) -> {
+                mIsOcclusionTransitionRunning =
+                        step.getTransitionState() == TransitionState.RUNNING;
+            };
+
     private final Consumer<TransitionStep> mLockscreenToOccludedTransition =
             (TransitionStep step) -> {
                 mIsOcclusionTransitionRunning =
@@ -734,7 +760,10 @@
             OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel,
             LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel,
             GoneToDreamingTransitionViewModel goneToDreamingTransitionViewModel,
+            GoneToDreamingLockscreenHostedTransitionViewModel
+                    goneToDreamingLockscreenHostedTransitionViewModel,
             LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel,
+            PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
             @Main CoroutineDispatcher mainDispatcher,
             KeyguardTransitionInteractor keyguardTransitionInteractor,
             DumpManager dumpManager,
@@ -761,7 +790,10 @@
         mOccludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel;
         mLockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel;
         mGoneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel;
+        mGoneToDreamingLockscreenHostedTransitionViewModel =
+                goneToDreamingLockscreenHostedTransitionViewModel;
         mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
+        mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel;
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
         mKeyguardInteractor = keyguardInteractor;
         mKeyguardViewConfigurator = keyguardViewConfigurator;
@@ -1091,6 +1123,24 @@
                 mDreamingToLockscreenTransitionTranslationY),
                 setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
 
+        // Gone -> Dreaming hosted in lockscreen
+        collectFlow(mView, mKeyguardTransitionInteractor
+                        .getGoneToDreamingLockscreenHostedTransition(),
+                mGoneToDreamingLockscreenHostedTransition, mMainDispatcher);
+        collectFlow(mView, mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha(),
+                setTransitionAlpha(mNotificationStackScrollLayoutController),
+                mMainDispatcher);
+
+        // Lockscreen -> Dreaming hosted in lockscreen
+        collectFlow(mView, mKeyguardTransitionInteractor
+                        .getLockscreenToDreamingLockscreenHostedTransition(),
+                mLockscreenToDreamingLockscreenHostedTransition, mMainDispatcher);
+
+        // Dreaming hosted in lockscreen -> Lockscreen
+        collectFlow(mView, mKeyguardTransitionInteractor
+                        .getDreamingLockscreenHostedToLockscreenTransition(),
+                mDreamingLockscreenHostedToLockscreenTransition, mMainDispatcher);
+
         // Occluded->Lockscreen
         collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(),
                 mOccludedToLockscreenTransition, mMainDispatcher);
@@ -1126,6 +1176,10 @@
         collectFlow(mView, mLockscreenToOccludedTransitionViewModel.lockscreenTranslationY(
                 mLockscreenToOccludedTransitionTranslationY),
                 setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
+
+        // Primary bouncer->Gone (ensures lockscreen content is not visible on successful auth)
+        collectFlow(mView, mPrimaryBouncerToGoneTransitionViewModel.getLockscreenAlpha(),
+                setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
     }
 
     @VisibleForTesting
@@ -1496,13 +1550,18 @@
         mAnimateNextPositionUpdate = false;
     }
 
+    private boolean shouldAnimateKeyguardStatusViewAlignment() {
+        // Do not animate when transitioning from Gone->DreamingLockscreenHosted
+        return !mIsGoneToDreamingLockscreenHostedTransitionRunning;
+    }
+
     private void updateClockAppearance() {
         int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
         boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
         boolean shouldAnimateClockChange = mScreenOffAnimationController.shouldAnimateClockChange();
         mKeyguardStatusViewController.displayClock(computeDesiredClockSize(),
                 shouldAnimateClockChange);
-        updateKeyguardStatusViewAlignment(/* animate= */true);
+        updateKeyguardStatusViewAlignment(/* animate= */shouldAnimateKeyguardStatusViewAlignment());
         int userSwitcherHeight = mKeyguardQsUserSwitchController != null
                 ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
         if (mKeyguardUserSwitcherController != null) {
@@ -1625,6 +1684,10 @@
             // overlap.
             return true;
         }
+        if (isActiveDreamLockscreenHosted()) {
+            // Dreaming hosted in lockscreen, no "visible" notifications. Safe to center the clock.
+            return true;
+        }
         if (mNotificationListContainer.hasPulsingNotifications()) {
             // Pulsing notification appears on the right. Move clock left to avoid overlap.
             return false;
@@ -1653,6 +1716,11 @@
         return mDozing && mDozeParameters.getAlwaysOn();
     }
 
+
+    private boolean isActiveDreamLockscreenHosted() {
+        return mKeyguardInteractor.isActiveDreamLockscreenHosted().getValue();
+    }
+
     private boolean hasVisibleNotifications() {
         return mNotificationStackScrollLayoutController
                 .getVisibleNotificationCount() != 0
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 31d4ab9..ea3a8f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -91,6 +91,7 @@
         NotificationSectionHeadersModule.class,
         NotificationListViewModelModule.class,
         ActivatableNotificationViewModelModule.class,
+        NotificationMemoryModule.class,
 })
 public interface NotificationsModule {
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index cc08778..adff681 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -471,7 +471,7 @@
         };
 
         final boolean wakingFromDream = mMode == MODE_WAKE_AND_UNLOCK_FROM_DREAM
-                && !mStatusBarStateController.isDozing();
+                && mPowerManager.isInteractive();
 
         if (mMode != MODE_NONE && !wakingFromDream) {
             wakeUp.run();
@@ -509,7 +509,7 @@
                     // later to awaken.
                 }
                 mNotificationShadeWindowController.setNotificationShadeFocusable(false);
-                mKeyguardViewMediator.onWakeAndUnlocking();
+                mKeyguardViewMediator.onWakeAndUnlocking(wakingFromDream);
                 Trace.endSection();
                 break;
             case MODE_ONLY_WAKE:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 1c3a8850..dabdcc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -46,7 +46,6 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.qs.SettingObserver;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.util.Utils;
 import com.android.systemui.util.settings.GlobalSettings;
@@ -68,17 +67,17 @@
     private final Context mContext;
     private final UserTracker mUserTracker;
     private final BroadcastDispatcher mBroadcastDispatcher;
-    private final SettingObserver mModeSetting;
-    private final SettingObserver mConfigSetting;
     private final NotificationManager mNoMan;
     private final AlarmManager mAlarmManager;
     private final SetupObserver mSetupObserver;
     private final UserManager mUserManager;
+    private final GlobalSettings mGlobalSettings;
 
     private int mUserId;
     private boolean mRegistered;
     private ZenModeConfig mConfig;
-    private int mZenMode;
+    // This value is changed in the main thread, but may be read in a background thread.
+    private volatile int mZenMode;
     private long mZenUpdateTime;
     private NotificationManager.Policy mConsolidatedNotificationPolicy;
 
@@ -111,18 +110,20 @@
         mContext = context;
         mBroadcastDispatcher = broadcastDispatcher;
         mUserTracker = userTracker;
-        mModeSetting = new SettingObserver(globalSettings, handler, Global.ZEN_MODE,
-                userTracker.getUserId()) {
+        mGlobalSettings = globalSettings;
+
+        ContentObserver modeContentObserver = new ContentObserver(handler) {
             @Override
-            protected void handleValueChanged(int value, boolean observedChange) {
+            public void onChange(boolean selfChange) {
+                int value = getModeSettingValueFromProvider();
+                Log.d(TAG, "Zen mode setting changed to " + value);
                 updateZenMode(value);
                 fireZenChanged(value);
             }
         };
-        mConfigSetting = new SettingObserver(globalSettings, handler, Global.ZEN_MODE_CONFIG_ETAG,
-                userTracker.getUserId()) {
+        ContentObserver configContentObserver = new ContentObserver(handler) {
             @Override
-            protected void handleValueChanged(int value, boolean observedChange) {
+            public void onChange(boolean selfChange) {
                 try {
                     Trace.beginSection("updateZenModeConfig");
                     updateZenModeConfig();
@@ -132,9 +133,9 @@
             }
         };
         mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
-        mModeSetting.setListening(true);
-        updateZenMode(mModeSetting.getValue());
-        mConfigSetting.setListening(true);
+        globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver);
+        updateZenMode(getModeSettingValueFromProvider());
+        globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG, configContentObserver);
         updateZenModeConfig();
         updateConsolidatedNotificationPolicy();
         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
@@ -146,6 +147,10 @@
         dumpManager.registerDumpable(getClass().getSimpleName(), this);
     }
 
+    private int getModeSettingValueFromProvider() {
+        return mGlobalSettings.getInt(Global.ZEN_MODE, /* default */ Global.ZEN_MODE_OFF);
+    }
+
     @Override
     public boolean isVolumeRestricted() {
         return mUserManager.hasUserRestriction(UserManager.DISALLOW_ADJUST_VOLUME,
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt
index 1e73cb3..1e65566 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt
@@ -79,15 +79,15 @@
 
     private val hapticsScale: Float
         get() {
-            val intensityString = SystemProperties.get("persist.unfold.haptics_scale", "0.1")
-            return intensityString.toFloatOrNull() ?: 0.1f
+            val intensityString = SystemProperties.get("persist.unfold.haptics_scale", "0.5")
+            return intensityString.toFloatOrNull() ?: 0.5f
         }
 
     private val hapticsScaleTick: Float
         get() {
             val intensityString =
-                SystemProperties.get("persist.unfold.haptics_scale_end_tick", "0.6")
-            return intensityString.toFloatOrNull() ?: 0.6f
+                SystemProperties.get("persist.unfold.haptics_scale_end_tick", "1.0")
+            return intensityString.toFloatOrNull() ?: 1.0f
         }
 
     private val primitivesCount: Int
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 87b2697..6219e4d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -945,6 +945,7 @@
                 showRingerDrawer();
             }
         });
+        updateSelectedRingerContainerDescription(mIsRingerDrawerOpen);
 
         mRingerDrawerVibrate.setOnClickListener(
                 new RingerDrawerItemClickListener(RINGER_MODE_VIBRATE));
@@ -1007,6 +1008,19 @@
                         : 0;
     }
 
+    @VisibleForTesting String getSelectedRingerContainerDescription() {
+        return mSelectedRingerContainer == null ? null :
+                mSelectedRingerContainer.getContentDescription().toString();
+    }
+
+    @VisibleForTesting void toggleRingerDrawer(boolean show) {
+        if (show) {
+            showRingerDrawer();
+        } else {
+            hideRingerDrawer();
+        }
+    }
+
     /** Animates in the ringer drawer. */
     private void showRingerDrawer() {
         if (mIsRingerDrawerOpen) {
@@ -1084,12 +1098,7 @@
                     .start();
         }
 
-        // When the ringer drawer is open, tapping the currently selected ringer will set the ringer
-        // to the current ringer mode. Change the content description to that, instead of the 'tap
-        // to change ringer mode' default.
-        mSelectedRingerContainer.setContentDescription(
-                mContext.getString(getStringDescriptionResourceForRingerMode(
-                        mState.ringerModeInternal)));
+        updateSelectedRingerContainerDescription(true);
 
         mIsRingerDrawerOpen = true;
     }
@@ -1135,14 +1144,38 @@
                 .translationY(0f)
                 .start();
 
-        // When the drawer is closed, tapping the selected ringer drawer will open it, allowing the
-        // user to change the ringer.
-        mSelectedRingerContainer.setContentDescription(
-                mContext.getString(R.string.volume_ringer_change));
+        updateSelectedRingerContainerDescription(false);
 
         mIsRingerDrawerOpen = false;
     }
 
+
+    /**
+     * @param open false to set the description when drawer is closed
+     */
+    private void updateSelectedRingerContainerDescription(boolean open) {
+        if (mState == null || mSelectedRingerContainer == null) return;
+
+        String currentMode = mContext.getString(getStringDescriptionResourceForRingerMode(
+                mState.ringerModeInternal));
+        String tapToSelect;
+
+        if (open) {
+            // When the ringer drawer is open, tapping the currently selected ringer will set the
+            // ringer to the current ringer mode. Change the content description to that, instead of
+            // the 'tap to change ringer mode' default.
+            tapToSelect = "";
+
+        } else {
+            // When the drawer is closed, tapping the selected ringer drawer will open it, allowing
+            // the user to change the ringer. The user needs to know that, and also the current mode
+            currentMode += ", ";
+            tapToSelect = mContext.getString(R.string.volume_ringer_change);
+        }
+
+        mSelectedRingerContainer.setContentDescription(currentMode + tapToSelect);
+    }
+
     private void initSettingsH(int lockTaskModeState) {
         if (mSettingsView != null) {
             mSettingsView.setVisibility(
@@ -1726,7 +1759,7 @@
         });
     }
 
-    private int getStringDescriptionResourceForRingerMode(int mode) {
+    @VisibleForTesting int getStringDescriptionResourceForRingerMode(int mode) {
         switch (mode) {
             case RINGER_MODE_SILENT:
                 return R.string.volume_ringer_status_silent;
@@ -1823,6 +1856,7 @@
             updateVolumeRowH(row);
         }
         updateRingerH();
+        updateSelectedRingerContainerDescription(mIsRingerDrawerOpen);
         mWindow.setTitle(composeWindowTitle());
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 58982d1..7ab8e8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -23,6 +23,7 @@
 
 import static com.android.internal.util.FunctionalUtils.ThrowingConsumer;
 import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
+import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -61,6 +62,7 @@
 import android.os.VibrationAttributes;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.Pair;
+import android.view.HapticFeedbackConstants;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -1128,6 +1130,36 @@
     }
 
     @Test
+    public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled_oneWayHapticsEnabled()
+            throws RemoteException {
+        when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true);
+        // Configure UdfpsView to accept the ACTION_DOWN event
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+        when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+        // GIVEN that the overlay is showing and a11y touch exploration enabled
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
+        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        // WHEN ACTION_HOVER is received
+        verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture());
+        MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0);
+        mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent);
+        enterEvent.recycle();
+        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0);
+        mHoverListenerCaptor.getValue().onHover(mUdfpsView, moveEvent);
+        moveEvent.recycle();
+
+        // THEN context click haptic is played
+        verify(mVibrator).performHapticFeedback(
+                any(),
+                eq(HapticFeedbackConstants.CONTEXT_CLICK)
+        );
+    }
+
+    @Test
     public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException {
         // Configure UdfpsView to accept the ACTION_DOWN event
         when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
@@ -1160,6 +1192,35 @@
     }
 
     @Test
+    public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled__oneWayHapticsEnabled()
+            throws RemoteException {
+        when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true);
+        // Configure UdfpsView to accept the ACTION_DOWN event
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+        when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+        // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        // WHEN ACTION_DOWN is received
+        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+        mBiometricExecutor.runAllReady();
+        downEvent.recycle();
+        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+        mBiometricExecutor.runAllReady();
+        moveEvent.recycle();
+
+        // THEN NO haptic played
+        verify(mVibrator, never()).performHapticFeedback(any(), anyInt());
+    }
+
+    @Test
     public void onTouch_withoutNewTouchDetection_shouldCallOldFingerprintManagerPath()
             throws RemoteException {
         // Disable new touch detection.
@@ -1514,4 +1575,45 @@
         // THEN is fingerDown should be FALSE
         assertFalse(mUdfpsController.isFingerDown());
     }
+
+    @Test
+    public void playHaptic_onAodInterrupt_oneWayHapticsDisabled_onAcquiredBad_usesVibrate()
+            throws RemoteException {
+        // GIVEN UDFPS overlay is showing
+        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        // GIVEN there's been an AoD interrupt
+        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
+        mScreenObserver.onScreenTurnedOn();
+        mUdfpsController.onAodInterrupt(0, 0, 0, 0);
+
+        // THEN vibrate is used
+        verify(mVibrator).vibrate(
+                anyInt(),
+                anyString(),
+                eq(UdfpsController.EFFECT_CLICK),
+                eq("aod-lock-icon-longpress"),
+                eq(UdfpsController.LOCK_ICON_VIBRATION_ATTRIBUTES)
+        );
+    }
+
+    @Test
+    public void playHaptic_onAodInterrupt_oneWayHapticsEnabled_onAcquiredBad_performHapticFeedback()
+            throws RemoteException {
+        when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true);
+        // GIVEN UDFPS overlay is showing
+        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        // GIVEN there's been an AoD interrupt
+        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
+        mScreenObserver.onScreenTurnedOn();
+        mUdfpsController.onAodInterrupt(0, 0, 0, 0);
+
+        // THEN vibrate is used
+        verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java
index 57d3a01..cbbbe52 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java
@@ -28,7 +28,9 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.complication.dagger.DreamClockTimeComplicationComponent;
 import com.android.systemui.condition.SelfExecutingMonitor;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.shared.condition.Monitor;
@@ -36,11 +38,10 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import javax.inject.Provider;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class DreamClockTimeComplicationTest extends SysuiTestCase {
@@ -55,8 +56,10 @@
     private DreamClockTimeComplication mComplication;
 
     @Mock
-    private Provider<DreamClockTimeComplication.DreamClockTimeViewHolder>
-            mDreamClockTimeViewHolderProvider;
+    private DreamClockTimeComplicationComponent.Factory mComponentFactory;
+
+    @Mock
+    private DreamClockTimeComplicationComponent mComponent;
 
     @Mock
     private DreamClockTimeComplication.DreamClockTimeViewHolder
@@ -71,12 +74,19 @@
     @Mock
     private ComplicationLayoutParams mLayoutParams;
 
+    @Mock
+    private DreamClockTimeComplication.DreamClockTimeViewController mViewController;
+
+    @Mock
+    private UiEventLogger mUiEventLogger;
+
     private Monitor mMonitor;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        when(mDreamClockTimeViewHolderProvider.get()).thenReturn(mDreamClockTimeViewHolder);
+        when(mComponentFactory.create()).thenReturn(mComponent);
+        when(mComponent.getViewHolder()).thenReturn(mDreamClockTimeViewHolder);
         mMonitor = SelfExecutingMonitor.createInstance();
     }
 
@@ -100,21 +110,21 @@
     @Test
     public void testComplicationRequiredTypeAvailability() {
         final DreamClockTimeComplication complication =
-                new DreamClockTimeComplication(mDreamClockTimeViewHolderProvider);
+                new DreamClockTimeComplication(mComponentFactory);
         assertEquals(Complication.COMPLICATION_TYPE_TIME,
                 complication.getRequiredTypeAvailability());
     }
 
     /**
      * Verifies {@link DreamClockTimeComplication.DreamClockTimeViewHolder} is obtainable from its
-     * provider when the complication creates view.
+     * component when the complication creates view.
      */
     @Test
-    public void testComplicationViewHolderProviderOnCreateView() {
+    public void testComplicationViewHolderComponentOnCreateView() {
         final DreamClockTimeComplication complication =
-                new DreamClockTimeComplication(mDreamClockTimeViewHolderProvider);
+                new DreamClockTimeComplication(mComponentFactory);
         final Complication.ViewHolder viewHolder = complication.createView(mComplicationViewModel);
-        verify(mDreamClockTimeViewHolderProvider).get();
+        verify(mComponent).getViewHolder();
         assertThat(viewHolder).isEqualTo(mDreamClockTimeViewHolder);
     }
 
@@ -125,8 +135,23 @@
     @Test
     public void testComplicationViewHolderContentAccessors() {
         final DreamClockTimeComplication.DreamClockTimeViewHolder viewHolder =
-                new DreamClockTimeComplication.DreamClockTimeViewHolder(mView, mLayoutParams);
+                new DreamClockTimeComplication.DreamClockTimeViewHolder(mView, mLayoutParams,
+                        mViewController);
         assertThat(viewHolder.getView()).isEqualTo(mView);
         assertThat(viewHolder.getLayoutParams()).isEqualTo(mLayoutParams);
     }
+
+    @Test
+    public void testClick_logUiEvent() {
+        final DreamClockTimeComplication.DreamClockTimeViewController controller =
+                new DreamClockTimeComplication.DreamClockTimeViewController(mView, mUiEventLogger);
+        controller.onViewAttached();
+
+        final ArgumentCaptor<View.OnClickListener> clickListenerCaptor =
+                ArgumentCaptor.forClass(View.OnClickListener.class);
+        verify(mView).setOnClickListener(clickListenerCaptor.capture());
+
+        clickListenerCaptor.getValue().onClick(mView);
+        verify(mUiEventLogger).log(DreamOverlayUiEvent.DREAM_CLOCK_TAPPED);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
index 63b0b25..2207180 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
@@ -234,9 +234,7 @@
         verify(mHomeControlsView).setOnClickListener(clickListenerCaptor.capture());
 
         clickListenerCaptor.getValue().onClick(mHomeControlsView);
-        verify(mUiEventLogger).log(
-                DreamHomeControlsComplication.DreamHomeControlsChipViewController
-                        .DreamOverlayEvent.DREAM_HOME_CONTROLS_TAPPED);
+        verify(mUiEventLogger).log(DreamOverlayUiEvent.DREAM_HOME_CONTROLS_TAPPED);
     }
 
     private void setHaveFavorites(boolean value) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
index 71d2ec1..bd3d09d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
@@ -16,8 +16,6 @@
 import com.android.systemui.activity.SingleActivityFactory
 import com.android.systemui.controls.CustomIconCache
 import com.android.systemui.controls.controller.ControlsControllerImpl
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
@@ -46,7 +44,6 @@
     }
 
     private val uiExecutor = FakeExecutor(FakeSystemClock())
-    private val featureFlags = FakeFeatureFlags()
 
     @Mock lateinit var controller: ControlsControllerImpl
 
@@ -65,7 +62,6 @@
         ActivityTestRule(
             /* activityFactory= */ SingleActivityFactory {
                 TestableControlsEditingActivity(
-                    featureFlags,
                     uiExecutor,
                     controller,
                     userTracker,
@@ -81,8 +77,6 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-
-        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, false)
     }
 
     @Test
@@ -101,16 +95,7 @@
     }
 
     @Test
-    fun testNewFlowDisabled_addControlsButton_gone() {
-        with(launchActivity()) {
-            val addControlsButton = requireViewById<Button>(R.id.addControls)
-            assertThat(addControlsButton.visibility).isEqualTo(View.GONE)
-        }
-    }
-
-    @Test
-    fun testNewFlowEnabled_addControlsButton_visible() {
-        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+    fun testAddControlsButton_visible() {
         with(launchActivity()) {
             val addControlsButton = requireViewById<Button>(R.id.addControls)
             assertThat(addControlsButton.visibility).isEqualTo(View.VISIBLE)
@@ -120,7 +105,6 @@
 
     @Test
     fun testNotLaunchFromFavoriting_saveButton_disabled() {
-        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
         with(launchActivity(isFromFavoriting = false)) {
             val saveButton = requireViewById<Button>(R.id.done)
             assertThat(saveButton.isEnabled).isFalse()
@@ -129,7 +113,6 @@
 
     @Test
     fun testLaunchFromFavoriting_saveButton_enabled() {
-        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
         with(launchActivity(isFromFavoriting = true)) {
             val saveButton = requireViewById<Button>(R.id.done)
             assertThat(saveButton.isEnabled).isTrue()
@@ -138,7 +121,6 @@
 
     @Test
     fun testNotFromFavoriting_addControlsPressed_launchesFavouriting() {
-        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
         with(launchActivity(isFromFavoriting = false)) {
             val addControls = requireViewById<Button>(R.id.addControls)
 
@@ -177,7 +159,6 @@
         )
 
     class TestableControlsEditingActivity(
-        featureFlags: FakeFeatureFlags,
         executor: FakeExecutor,
         controller: ControlsControllerImpl,
         userTracker: UserTracker,
@@ -186,7 +167,6 @@
         private val latch: CountDownLatch
     ) :
         ControlsEditingActivity(
-            featureFlags,
             executor,
             controller,
             userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
index f11c296..70d93a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
@@ -17,14 +17,11 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.activity.SingleActivityFactory
 import com.android.systemui.controls.ControlStatus
-import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.controller.ControlsControllerImpl
 import com.android.systemui.controls.controller.createLoadDataObject
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
@@ -73,8 +70,6 @@
 
     @Mock lateinit var controller: ControlsControllerImpl
 
-    @Mock lateinit var listingController: ControlsListingController
-
     @Mock lateinit var userTracker: UserTracker
 
     private var latch: CountDownLatch = CountDownLatch(1)
@@ -82,9 +77,6 @@
     @Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
     @Captor private lateinit var captureCallback: ArgumentCaptor<OnBackInvokedCallback>
     @Captor
-    private lateinit var listingCallback:
-        ArgumentCaptor<ControlsListingController.ControlsListingCallback>
-    @Captor
     private lateinit var controlsCallback: ArgumentCaptor<Consumer<ControlsController.LoadData>>
 
     @Rule
@@ -93,10 +85,8 @@
         ActivityTestRule(
             /* activityFactory= */ SingleActivityFactory {
                 TestableControlsFavoritingActivity(
-                    featureFlags,
                     executor,
                     controller,
-                    listingController,
                     userTracker,
                     mockDispatcher,
                     latch
@@ -109,7 +99,6 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, false)
     }
 
     // b/259549854 to root-cause and fix
@@ -130,14 +119,8 @@
     }
 
     @Test
-    fun testNewFlowEnabled_buttons() {
-        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+    fun testButtons() {
         with(launchActivity()) {
-            verify(listingController).addCallback(listingCallback.capture())
-            listingCallback.value.onServicesUpdated(
-                listOf(mock(ControlsServiceInfo::class.java), mock(ControlsServiceInfo::class.java))
-            )
-
             val rearrangeButton = requireViewById<Button>(R.id.rearrange)
             assertThat(rearrangeButton.visibility).isEqualTo(View.VISIBLE)
             assertThat(rearrangeButton.isEnabled).isFalse()
@@ -149,36 +132,8 @@
     }
 
     @Test
-    fun testNewFlowDisabled_buttons() {
+    fun testRearrangePressed_savesAndlaunchesActivity() {
         with(launchActivity()) {
-            verify(listingController).addCallback(listingCallback.capture())
-            activityRule.runOnUiThread {
-                listingCallback.value.onServicesUpdated(
-                    listOf(
-                        mock(ControlsServiceInfo::class.java),
-                        mock(ControlsServiceInfo::class.java)
-                    )
-                )
-            }
-
-            val rearrangeButton = requireViewById<Button>(R.id.rearrange)
-            assertThat(rearrangeButton.visibility).isEqualTo(View.GONE)
-            assertThat(rearrangeButton.isEnabled).isFalse()
-
-            val otherAppsButton = requireViewById<Button>(R.id.other_apps)
-            otherAppsButton.waitForPost()
-            assertThat(otherAppsButton.visibility).isEqualTo(View.VISIBLE)
-        }
-    }
-
-    @Test
-    fun testNewFlowEnabled_rearrangePressed_savesAndlaunchesActivity() {
-        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
-        with(launchActivity()) {
-            verify(listingController).addCallback(capture(listingCallback))
-            listingCallback.value.onServicesUpdated(
-                listOf(mock(ControlsServiceInfo::class.java), mock(ControlsServiceInfo::class.java))
-            )
             verify(controller).loadForComponent(any(), capture(controlsCallback), any())
             activityRule.runOnUiThread {
                 controlsCallback.value.accept(
@@ -224,19 +179,15 @@
         )
 
     class TestableControlsFavoritingActivity(
-        featureFlags: FeatureFlags,
         executor: Executor,
         controller: ControlsControllerImpl,
-        listingController: ControlsListingController,
         userTracker: UserTracker,
         private val mockDispatcher: OnBackInvokedDispatcher,
         private val latch: CountDownLatch
     ) :
         ControlsFavoritingActivity(
-            featureFlags,
             executor,
             controller,
-            listingController,
             userTracker,
         ) {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
index 272f589..7ac1953 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
@@ -22,8 +22,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.FakeSharedPreferences
@@ -42,8 +40,6 @@
 
     @Mock private lateinit var userTracker: UserTracker
 
-    private val featureFlags = FakeFeatureFlags()
-
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -52,7 +48,6 @@
             arrayOf<String>()
         )
         whenever(userTracker.userId).thenReturn(0)
-        featureFlags.set(Flags.APP_PANELS_REMOVE_APPS_ALLOWED, true)
     }
 
     @Test
@@ -132,25 +127,8 @@
         assertThat(sharedPrefs.getStringSet(KEY, null)).isEmpty()
     }
 
-    @Test
-    fun testSetAuthorizedPackageAfterFeatureDisabled() {
-        mContext.orCreateTestableResources.addOverride(
-            R.array.config_controlsPreferredPackages,
-            arrayOf(TEST_PACKAGE)
-        )
-        val sharedPrefs = FakeSharedPreferences()
-        val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs))
-        val repository = createRepository(fileManager)
-
-        repository.removeAuthorizedPanels(setOf(TEST_PACKAGE))
-
-        featureFlags.set(Flags.APP_PANELS_REMOVE_APPS_ALLOWED, false)
-
-        assertThat(repository.getAuthorizedPanels()).isEqualTo(setOf(TEST_PACKAGE))
-    }
-
     private fun createRepository(userFileManager: UserFileManager): AuthorizedPanelsRepositoryImpl {
-        return AuthorizedPanelsRepositoryImpl(mContext, userFileManager, userTracker, featureFlags)
+        return AuthorizedPanelsRepositoryImpl(mContext, userFileManager, userTracker)
     }
 
     private class FakeUserFileManager(private val sharedPrefs: Map<Int, SharedPreferences>) :
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
index 0c7b9cb..6230ea7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
@@ -22,7 +22,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl
@@ -103,36 +102,18 @@
     }
 
     @Test
-    fun testFeatureEnabled_shouldAddDefaultPanelDefaultsToTrue() {
-        featureFlags.set(Flags.APP_PANELS_REMOVE_APPS_ALLOWED, true)
-
+    fun testShouldAddDefaultPanelDefaultsToTrue() {
         assertThat(repository.shouldAddDefaultComponent()).isTrue()
     }
 
     @Test
-    fun testFeatureDisabled_shouldAddDefaultPanelDefaultsToTrue() {
-        featureFlags.set(Flags.APP_PANELS_REMOVE_APPS_ALLOWED, false)
-
-        assertThat(repository.shouldAddDefaultComponent()).isTrue()
-    }
-
-    @Test
-    fun testFeatureEnabled_shouldAddDefaultPanelChecked() {
-        featureFlags.set(Flags.APP_PANELS_REMOVE_APPS_ALLOWED, true)
+    fun testShouldAddDefaultPanelChecked() {
         repository.setShouldAddDefaultComponent(false)
 
         assertThat(repository.shouldAddDefaultComponent()).isFalse()
     }
 
     @Test
-    fun testFeatureDisabled_shouldAlwaysAddDefaultPanelAlwaysTrue() {
-        featureFlags.set(Flags.APP_PANELS_REMOVE_APPS_ALLOWED, false)
-        repository.setShouldAddDefaultComponent(false)
-
-        assertThat(repository.shouldAddDefaultComponent()).isTrue()
-    }
-
-    @Test
     fun testGetPreferredStructure_differentUserId() {
         sharedPreferences.savePanel(COMPONENT_A)
         whenever(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 0ffa2d7..d8d3f92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -275,7 +275,7 @@
         TestableLooper.get(this).processAllMessages();
 
         mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
-        mViewMediator.onWakeAndUnlocking();
+        mViewMediator.onWakeAndUnlocking(false);
         mViewMediator.onStartedWakingUp(OFF_BECAUSE_OF_USER, false);
         TestableLooper.get(this).processAllMessages();
 
@@ -337,6 +337,8 @@
         mViewMediator.onSystemReady();
         TestableLooper.get(this).processAllMessages();
 
+        when(mPowerManager.isInteractive()).thenReturn(true);
+
         // Given device is dreaming
         when(mUpdateMonitor.isDreaming()).thenReturn(true);
 
@@ -707,14 +709,14 @@
 
     @Test
     public void testWakeAndUnlocking() {
-        mViewMediator.onWakeAndUnlocking();
+        mViewMediator.onWakeAndUnlocking(false);
         verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
     }
 
     @Test
     public void testWakeAndUnlockingOverDream() {
         // Send signal to wake
-        mViewMediator.onWakeAndUnlocking();
+        mViewMediator.onWakeAndUnlocking(true);
 
         // Ensure not woken up yet
         verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
@@ -743,7 +745,7 @@
     @Test
     public void testWakeAndUnlockingOverDream_signalAuthenticateIfStillShowing() {
         // Send signal to wake
-        mViewMediator.onWakeAndUnlocking();
+        mViewMediator.onWakeAndUnlocking(true);
 
         // Ensure not woken up yet
         verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
@@ -773,6 +775,35 @@
     }
 
     @Test
+    public void testWakeAndUnlockingOverNonInteractiveDream_noWakeByKeyguardViewMediator() {
+        // Send signal to wake
+        mViewMediator.onWakeAndUnlocking(false);
+
+        // Ensure not woken up yet
+        verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
+
+        // Verify keyguard told of authentication
+        verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
+        mViewMediator.mViewMediatorCallback.keyguardDonePending(true,
+                mUpdateMonitor.getCurrentUser());
+        mViewMediator.mViewMediatorCallback.readyForKeyguardDone();
+        final ArgumentCaptor<Runnable> animationRunnableCaptor =
+                ArgumentCaptor.forClass(Runnable.class);
+        verify(mStatusBarKeyguardViewManager).startPreHideAnimation(
+                animationRunnableCaptor.capture());
+
+        when(mStatusBarStateController.isDreaming()).thenReturn(true);
+        when(mStatusBarStateController.isDozing()).thenReturn(false);
+        animationRunnableCaptor.getValue().run();
+
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
+        mViewMediator.mViewMediatorCallback.keyguardGone();
+
+        // Verify not woken up.
+        verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
+    }
+
+    @Test
     @TestableLooper.RunWithLooper(setAsMainLooper = true)
     public void testDoKeyguardWhileInteractive_resets() {
         mViewMediator.setShowingLocked(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index d01a46e..aa6bd4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -89,6 +89,8 @@
         FromAlternateBouncerTransitionInteractor
     private lateinit var fromPrimaryBouncerTransitionInteractor:
         FromPrimaryBouncerTransitionInteractor
+    private lateinit var fromDreamingLockscreenHostedTransitionInteractor:
+        FromDreamingLockscreenHostedTransitionInteractor
 
     @Before
     fun setUp() {
@@ -140,6 +142,15 @@
                 )
                 .apply { start() }
 
+        fromDreamingLockscreenHostedTransitionInteractor =
+            FromDreamingLockscreenHostedTransitionInteractor(
+                    scope = testScope,
+                    keyguardInteractor = createKeyguardInteractor(),
+                    transitionRepository = transitionRepository,
+                    transitionInteractor = transitionInteractor,
+                )
+                .apply { start() }
+
         fromAodTransitionInteractor =
             FromAodTransitionInteractor(
                     scope = testScope,
@@ -299,6 +310,38 @@
         }
 
     @Test
+    fun lockscreenToDreamingLockscreenHosted() =
+        testScope.runTest {
+            // GIVEN a device that is not dreaming or dozing
+            keyguardRepository.setDreamingWithOverlay(false)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            runCurrent()
+
+            // GIVEN a prior transition has run to LOCKSCREEN
+            runTransition(KeyguardState.GONE, KeyguardState.LOCKSCREEN)
+
+            // WHEN the device begins to dream and the dream is lockscreen hosted
+            keyguardRepository.setDreamingWithOverlay(true)
+            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to DREAMING_LOCKSCREEN_HOSTED should occur
+            assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun lockscreenToDozing() =
         testScope.runTest {
             // GIVEN a device with AOD not available
@@ -353,6 +396,149 @@
         }
 
     @Test
+    fun dreamingLockscreenHostedToLockscreen() =
+        testScope.runTest {
+            // GIVEN a device dreaming with the lockscreen hosted dream and not dozing
+            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            runCurrent()
+
+            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+            runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+            // WHEN the lockscreen hosted dream stops
+            keyguardRepository.setIsActiveDreamLockscreenHosted(false)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to Lockscreen should occur
+            assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun dreamingLockscreenHostedToGone() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+            runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+            // WHEN biometrics succeeds with wake and unlock from dream mode
+            keyguardRepository.setBiometricUnlockState(
+                BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+            )
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to Gone should occur
+            assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.to).isEqualTo(KeyguardState.GONE)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun dreamingLockscreenHostedToPrimaryBouncer() =
+        testScope.runTest {
+            // GIVEN a device dreaming with lockscreen hosted dream and not dozing
+            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+            runCurrent()
+
+            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+            runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+            // WHEN the primary bouncer is set to show
+            bouncerRepository.setPrimaryShow(true)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to PRIMARY_BOUNCER should occur
+            assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun dreamingLockscreenHostedToDozing() =
+        testScope.runTest {
+            // GIVEN a device is dreaming with lockscreen hosted dream
+            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+            runCurrent()
+
+            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+            runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+            // WHEN the device begins to sleep
+            keyguardRepository.setIsActiveDreamLockscreenHosted(false)
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.INITIALIZED, to = DozeStateModel.DOZE)
+            )
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to DOZING should occur
+            assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.to).isEqualTo(KeyguardState.DOZING)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun dreamingLockscreenHostedToOccluded() =
+        testScope.runTest {
+            // GIVEN device is dreaming with lockscreen hosted dream and not occluded
+            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            runCurrent()
+
+            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+            runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+            // WHEN the keyguard is occluded and the lockscreen hosted dream stops
+            keyguardRepository.setIsActiveDreamLockscreenHosted(false)
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to OCCLUDED should occur
+            assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun dozingToLockscreen() =
         testScope.runTest {
             // GIVEN a prior transition has run to DOZING
@@ -533,6 +719,38 @@
         }
 
     @Test
+    fun goneToDreamingLockscreenHosted() =
+        testScope.runTest {
+            // GIVEN a device that is not dreaming or dozing
+            keyguardRepository.setDreamingWithOverlay(false)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            runCurrent()
+
+            // GIVEN a prior transition has run to GONE
+            runTransition(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
+
+            // WHEN the device begins to dream with the lockscreen hosted dream
+            keyguardRepository.setDreamingWithOverlay(true)
+            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to DREAMING_LOCKSCREEN_HOSTED should occur
+            assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.GONE)
+            assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun alternateBouncerToPrimaryBouncer() =
         testScope.runTest {
             // GIVEN a prior transition has run to ALTERNATE_BOUNCER
@@ -726,6 +944,34 @@
         }
 
     @Test
+    fun primaryBouncerToDreamingLockscreenHosted() =
+        testScope.runTest {
+            // GIVEN device dreaming with the lockscreen hosted dream and not dozing
+            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+
+            // GIVEN a prior transition has run to PRIMARY_BOUNCER
+            bouncerRepository.setPrimaryShow(true)
+            runTransition(KeyguardState.DREAMING_LOCKSCREEN_HOSTED, KeyguardState.PRIMARY_BOUNCER)
+
+            // WHEN the primary bouncer stops showing and lockscreen hosted dream still active
+            bouncerRepository.setPrimaryShow(false)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition back to DREAMING_LOCKSCREEN_HOSTED should occur
+            assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun occludedToGone() =
         testScope.runTest {
             // GIVEN a device on lockscreen
@@ -831,7 +1077,7 @@
                 withArgCaptor<TransitionInfo> {
                     verify(transitionRepository).startTransition(capture(), anyBoolean())
                 }
-            // THEN a transition to AlternateBouncer should occur
+            // THEN a transition to OCCLUDED should occur
             assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
             assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
             assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
@@ -840,6 +1086,61 @@
             coroutineContext.cancelChildren()
         }
 
+    @Test
+    fun dozingToOccluded() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to DOZING
+            runTransition(KeyguardState.LOCKSCREEN, KeyguardState.DOZING)
+            runCurrent()
+
+            // WHEN the keyguard is occluded and device wakes up
+            keyguardRepository.setKeyguardOccluded(true)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to OCCLUDED should occur
+            assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.DOZING)
+            assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun aodToOccluded() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to AOD
+            runTransition(KeyguardState.LOCKSCREEN, KeyguardState.AOD)
+            runCurrent()
+
+            // WHEN the keyguard is occluded and aod ends
+            keyguardRepository.setKeyguardOccluded(true)
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(
+                    from = DozeStateModel.DOZE_AOD,
+                    to = DozeStateModel.FINISH,
+                )
+            )
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to OCCLUDED should occur
+            assertThat(info.ownerName).isEqualTo("FromAodTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.AOD)
+            assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
     private fun startingToWake() =
         WakefulnessModel(
             WakefulnessState.STARTING_TO_WAKE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index d8c78eb..904662e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -31,8 +32,6 @@
 import com.android.systemui.util.mockito.whenever
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
@@ -75,9 +74,7 @@
     @Test
     fun bouncerAlpha() =
         runTest(UnconfinedTestDispatcher()) {
-            val values = mutableListOf<Float>()
-
-            val job = underTest.bouncerAlpha.onEach { values.add(it) }.launchIn(this)
+            val values by collectValues(underTest.bouncerAlpha)
 
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             repository.sendTransitionStep(step(0.3f))
@@ -85,16 +82,12 @@
 
             assertThat(values.size).isEqualTo(3)
             values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
-
-            job.cancel()
         }
 
     @Test
     fun bouncerAlpha_runDimissFromKeyguard() =
         runTest(UnconfinedTestDispatcher()) {
-            val values = mutableListOf<Float>()
-
-            val job = underTest.bouncerAlpha.onEach { values.add(it) }.launchIn(this)
+            val values by collectValues(underTest.bouncerAlpha)
 
             whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
 
@@ -104,16 +97,52 @@
 
             assertThat(values.size).isEqualTo(3)
             values.forEach { assertThat(it).isEqualTo(0f) }
+        }
 
-            job.cancel()
+    @Test
+    fun lockscreenAlpha() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values by collectValues(underTest.lockscreenAlpha)
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(1f))
+
+            assertThat(values.size).isEqualTo(2)
+            values.forEach { assertThat(it).isEqualTo(0f) }
+        }
+
+    @Test
+    fun lockscreenAlpha_runDimissFromKeyguard() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values by collectValues(underTest.lockscreenAlpha)
+
+            whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(1f))
+
+            assertThat(values.size).isEqualTo(2)
+            values.forEach { assertThat(it).isEqualTo(1f) }
+        }
+
+    @Test
+    fun lockscreenAlpha_leaveShadeOpen() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values by collectValues(underTest.lockscreenAlpha)
+
+            whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(true)
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(1f))
+
+            assertThat(values.size).isEqualTo(2)
+            values.forEach { assertThat(it).isEqualTo(1f) }
         }
 
     @Test
     fun scrimAlpha_runDimissFromKeyguard() =
         runTest(UnconfinedTestDispatcher()) {
-            val values = mutableListOf<ScrimAlpha>()
-
-            val job = underTest.scrimAlpha.onEach { values.add(it) }.launchIn(this)
+            val values by collectValues(underTest.scrimAlpha)
 
             whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
 
@@ -124,16 +153,12 @@
 
             assertThat(values.size).isEqualTo(4)
             values.forEach { assertThat(it).isEqualTo(ScrimAlpha()) }
-
-            job.cancel()
         }
 
     @Test
     fun scrimBehindAlpha_leaveShadeOpen() =
         runTest(UnconfinedTestDispatcher()) {
-            val values = mutableListOf<ScrimAlpha>()
-
-            val job = underTest.scrimAlpha.onEach { values.add(it) }.launchIn(this)
+            val values by collectValues(underTest.scrimAlpha)
 
             whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(true)
 
@@ -146,16 +171,12 @@
             values.forEach {
                 assertThat(it).isEqualTo(ScrimAlpha(notificationsAlpha = 1f, behindAlpha = 1f))
             }
-
-            job.cancel()
         }
 
     @Test
     fun scrimBehindAlpha_doNotLeaveShadeOpen() =
         runTest(UnconfinedTestDispatcher()) {
-            val values = mutableListOf<ScrimAlpha>()
-
-            val job = underTest.scrimAlpha.onEach { values.add(it) }.launchIn(this)
+            val values by collectValues(underTest.scrimAlpha)
 
             whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
 
@@ -169,8 +190,6 @@
             values.forEach { assertThat(it.frontAlpha).isEqualTo(0f) }
             values.forEach { assertThat(it.behindAlpha).isIn(Range.closed(0f, 1f)) }
             assertThat(values[3].behindAlpha).isEqualTo(0f)
-
-            job.cancel()
         }
 
     private fun step(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index c65a2d3..d933b57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -702,9 +702,10 @@
     // region updateNoteTaskAsUser
     @Test
     fun updateNoteTaskAsUser_sameUser_shouldUpdateShortcuts() {
-        val user = userTracker.userHandle
+        val user = UserHandle.CURRENT
         val controller = spy(createNoteTaskController())
         doNothing().whenever(controller).updateNoteTaskAsUserInternal(any())
+        whenever(controller.getCurrentRunningUser()).thenReturn(user)
 
         controller.updateNoteTaskAsUser(user)
 
@@ -714,10 +715,10 @@
 
     @Test
     fun updateNoteTaskAsUser_differentUser_shouldUpdateShortcutsInUserProcess() {
-        // FakeUserTracker will default to UserHandle.SYSTEM.
         val user = UserHandle.CURRENT
         val controller = spy(createNoteTaskController(isEnabled = true))
         doNothing().whenever(controller).updateNoteTaskAsUserInternal(any())
+        whenever(controller.getCurrentRunningUser()).thenReturn(UserHandle.SYSTEM)
 
         controller.updateNoteTaskAsUser(user)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index 826a6cc..56e3e96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -22,11 +22,13 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.scene.shared.model.SceneTransitionModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -91,11 +93,30 @@
         val sceneTransitionProgress by collectLastValue(underTest.transitionProgress)
         assertThat(sceneTransitionProgress).isEqualTo(1f)
 
-        underTest.setSceneTransitionProgress(0.1f)
+        val transitionState =
+            MutableStateFlow<ObservableTransitionState>(
+                ObservableTransitionState.Idle(SceneKey.Lockscreen)
+            )
+        underTest.setTransitionState(transitionState)
+        assertThat(sceneTransitionProgress).isEqualTo(1f)
+
+        val progress = MutableStateFlow(1f)
+        transitionState.value =
+            ObservableTransitionState.Transition(
+                fromScene = SceneKey.Lockscreen,
+                toScene = SceneKey.Shade,
+                progress = progress,
+            )
+        assertThat(sceneTransitionProgress).isEqualTo(1f)
+
+        progress.value = 0.1f
         assertThat(sceneTransitionProgress).isEqualTo(0.1f)
 
-        underTest.setSceneTransitionProgress(0.9f)
+        progress.value = 0.9f
         assertThat(sceneTransitionProgress).isEqualTo(0.9f)
+
+        underTest.setTransitionState(null)
+        assertThat(sceneTransitionProgress).isEqualTo(1f)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 13a602d..c193d83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -22,11 +22,13 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.scene.shared.model.SceneTransitionModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -37,7 +39,8 @@
 class SceneInteractorTest : SysuiTestCase() {
 
     private val utils = SceneTestUtils(this)
-    private val underTest = utils.sceneInteractor()
+    private val repository = utils.fakeSceneContainerRepository()
+    private val underTest = utils.sceneInteractor(repository = repository)
 
     @Test
     fun allSceneKeys() {
@@ -55,11 +58,20 @@
 
     @Test
     fun sceneTransitionProgress() = runTest {
-        val progress by collectLastValue(underTest.transitionProgress)
-        assertThat(progress).isEqualTo(1f)
+        val transitionProgress by collectLastValue(underTest.transitionProgress)
+        assertThat(transitionProgress).isEqualTo(1f)
 
-        underTest.setSceneTransitionProgress(0.55f)
-        assertThat(progress).isEqualTo(0.55f)
+        val progress = MutableStateFlow(0.55f)
+        repository.setTransitionState(
+            MutableStateFlow(
+                ObservableTransitionState.Transition(
+                    fromScene = SceneKey.Lockscreen,
+                    toScene = SceneKey.Shade,
+                    progress = progress,
+                ),
+            )
+        )
+        assertThat(transitionProgress).isEqualTo(0.55f)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index c759e07..40d9cc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -99,12 +99,14 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.ui.view.KeyguardRootView;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
 import com.android.systemui.media.controls.ui.KeyguardMediaController;
 import com.android.systemui.media.controls.ui.MediaHierarchyManager;
@@ -297,7 +299,10 @@
     @Mock protected LockscreenToOccludedTransitionViewModel
             mLockscreenToOccludedTransitionViewModel;
     @Mock protected GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
-
+    @Mock protected GoneToDreamingLockscreenHostedTransitionViewModel
+            mGoneToDreamingLockscreenHostedTransitionViewModel;
+    @Mock protected PrimaryBouncerToGoneTransitionViewModel
+            mPrimaryBouncerToGoneTransitionViewModel;
     @Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     @Mock protected KeyguardLongPressViewModel mKeyuardLongPressViewModel;
     @Mock protected AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -478,6 +483,20 @@
         when(mGoneToDreamingTransitionViewModel.lockscreenTranslationY(anyInt()))
                 .thenReturn(emptyFlow());
 
+        // Gone->Dreaming lockscreen hosted
+        when(mKeyguardTransitionInteractor.getGoneToDreamingLockscreenHostedTransition())
+                .thenReturn(emptyFlow());
+        when(mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha())
+                .thenReturn(emptyFlow());
+
+        // Dreaming lockscreen hosted->Lockscreen
+        when(mKeyguardTransitionInteractor.getDreamingLockscreenHostedToLockscreenTransition())
+                .thenReturn(emptyFlow());
+
+        // Lockscreen->Dreaming lockscreen hosted
+        when(mKeyguardTransitionInteractor.getLockscreenToDreamingLockscreenHostedTransition())
+                .thenReturn(emptyFlow());
+
         // Lockscreen->Occluded
         when(mKeyguardTransitionInteractor.getLockscreenToOccludedTransition())
                 .thenReturn(emptyFlow());
@@ -486,6 +505,10 @@
         when(mLockscreenToOccludedTransitionViewModel.lockscreenTranslationY(anyInt()))
                 .thenReturn(emptyFlow());
 
+        // Primary Bouncer->Gone
+        when(mPrimaryBouncerToGoneTransitionViewModel.getLockscreenAlpha())
+                .thenReturn(emptyFlow());
+
         NotificationWakeUpCoordinator coordinator =
                 new NotificationWakeUpCoordinator(
                         mDumpManager,
@@ -613,7 +636,9 @@
                 mOccludedToLockscreenTransitionViewModel,
                 mLockscreenToDreamingTransitionViewModel,
                 mGoneToDreamingTransitionViewModel,
+                mGoneToDreamingLockscreenHostedTransitionViewModel,
                 mLockscreenToOccludedTransitionViewModel,
+                mPrimaryBouncerToGoneTransitionViewModel,
                 mMainDispatcher,
                 mKeyguardTransitionInteractor,
                 mDumpManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 91aa138..481f7f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -199,7 +199,7 @@
         mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
                 BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
 
-        verify(mKeyguardViewMediator).onWakeAndUnlocking();
+        verify(mKeyguardViewMediator).onWakeAndUnlocking(false);
         assertThat(mBiometricUnlockController.getMode())
                 .isEqualTo(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
     }
@@ -217,7 +217,7 @@
         mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
                 BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
 
-        verify(mKeyguardViewMediator).onWakeAndUnlocking();
+        verify(mKeyguardViewMediator).onWakeAndUnlocking(false);
         assertThat(mBiometricUnlockController.getMode())
                 .isEqualTo(MODE_WAKE_AND_UNLOCK);
     }
@@ -671,8 +671,9 @@
         when(mWakefulnessLifecycle.getLastWakeReason())
                 .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
         givenDreamingLocked();
+        when(mPowerManager.isInteractive()).thenReturn(true);
         mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT, true);
-        verify(mKeyguardViewMediator).onWakeAndUnlocking();
+        verify(mKeyguardViewMediator).onWakeAndUnlocking(true);
         // Ensure that the power hasn't been told to wake up yet.
         verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
index c06dbdc..7f3d4b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
@@ -14,6 +14,7 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
@@ -24,10 +25,10 @@
 
 import android.app.NotificationManager;
 import android.os.Handler;
-import android.os.Looper;
 import android.provider.Settings;
 import android.service.notification.ZenModeConfig;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
 import androidx.test.filters.SmallTest;
@@ -45,6 +46,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
@@ -61,9 +65,10 @@
     DumpManager mDumpManager;
     @Mock
     UserTracker mUserTracker;
-
     private ZenModeControllerImpl mController;
 
+    private final FakeSettings mGlobalSettings = new FakeSettings();
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -72,10 +77,10 @@
 
         mController = new ZenModeControllerImpl(
                 mContext,
-                Handler.createAsync(Looper.myLooper()),
+                Handler.createAsync(TestableLooper.get(this).getLooper()),
                 mBroadcastDispatcher,
                 mDumpManager,
-                new FakeSettings(),
+                mGlobalSettings,
                 mUserTracker);
     }
 
@@ -131,4 +136,48 @@
         mController.addCallback(null);
         mController.fireConfigChanged(null);
     }
+
+    @Test
+    public void testModeChange() {
+        List<Integer> states = List.of(
+                Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                Settings.Global.ZEN_MODE_NO_INTERRUPTIONS,
+                Settings.Global.ZEN_MODE_ALARMS,
+                Settings.Global.ZEN_MODE_ALARMS
+        );
+
+        for (Integer state : states) {
+            mGlobalSettings.putInt(Settings.Global.ZEN_MODE, state);
+            TestableLooper.get(this).processAllMessages();
+            assertEquals(state.intValue(), mController.getZen());
+        }
+    }
+
+    @Test
+    public void testModeChange_callbackNotified() {
+        final AtomicInteger currentState = new AtomicInteger(-1);
+
+        ZenModeController.Callback callback = new Callback() {
+            @Override
+            public void onZenChanged(int zen) {
+                currentState.set(zen);
+            }
+        };
+
+        mController.addCallback(callback);
+
+        List<Integer> states = List.of(
+                Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                Settings.Global.ZEN_MODE_NO_INTERRUPTIONS,
+                Settings.Global.ZEN_MODE_ALARMS,
+                Settings.Global.ZEN_MODE_ALARMS
+        );
+
+        for (Integer state : states) {
+            mGlobalSettings.putInt(Settings.Global.ZEN_MODE, state);
+            TestableLooper.get(this).processAllMessages();
+            assertEquals(state.intValue(), currentState.get());
+        }
+
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index c819108..ee11cb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -16,14 +16,20 @@
 
 package com.android.systemui.volume;
 
+import static android.media.AudioManager.RINGER_MODE_NORMAL;
+import static android.media.AudioManager.RINGER_MODE_SILENT;
+import static android.media.AudioManager.RINGER_MODE_VIBRATE;
+
 import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
 import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN;
 import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN;
 import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotSame;
 import static junit.framework.Assert.assertTrue;
 
+import static org.junit.Assume.assumeNotNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -360,7 +366,7 @@
     public void testSelectVibrateFromDrawer_OnewayAPI_On() {
         mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
         final State initialUnsetState = new State();
-        initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
+        initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
         mDialog.onStateChangedH(initialUnsetState);
 
         mActiveRinger.performClick();
@@ -390,7 +396,7 @@
     public void testSelectMuteFromDrawer_OnewayAPI_On() {
         mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
         final State initialUnsetState = new State();
-        initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
+        initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
         mDialog.onStateChangedH(initialUnsetState);
 
         mActiveRinger.performClick();
@@ -428,7 +434,7 @@
 
         // Make sure we've actually changed the ringer mode.
         verify(mVolumeDialogController, times(1)).setRingerMode(
-                AudioManager.RINGER_MODE_NORMAL, false);
+                RINGER_MODE_NORMAL, false);
     }
 
     /**
@@ -625,6 +631,88 @@
         }
     }
 
+    private enum RingerDrawerState {INIT, OPEN, CLOSE}
+
+    @Test
+    public void ringerModeNormal_ringerContainerDescribesItsState() {
+        assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.INIT);
+    }
+
+    @Test
+    public void ringerModeSilent_ringerContainerDescribesItsState() {
+        assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.INIT);
+    }
+
+    @Test
+    public void ringerModeVibrate_ringerContainerDescribesItsState() {
+        assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.INIT);
+    }
+
+    @Test
+    public void ringerModeNormal_openDrawer_ringerContainerDescribesItsState() {
+        assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.OPEN);
+    }
+
+    @Test
+    public void ringerModeSilent_openDrawer_ringerContainerDescribesItsState() {
+        assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.OPEN);
+    }
+
+    @Test
+    public void ringerModeVibrate_openDrawer_ringerContainerDescribesItsState() {
+        assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.OPEN);
+    }
+
+    @Test
+    public void ringerModeNormal_closeDrawer_ringerContainerDescribesItsState() {
+        assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.CLOSE);
+    }
+
+    @Test
+    public void ringerModeSilent_closeDrawer_ringerContainerDescribesItsState() {
+        assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.CLOSE);
+    }
+
+    @Test
+    public void ringerModeVibrate_closeDrawer_ringerContainerDescribesItsState() {
+        assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.CLOSE);
+    }
+
+    /**
+     * The content description should include ringer state, and the correct one.
+     */
+    private void assertRingerContainerDescribesItsState(int ringerMode,
+            RingerDrawerState drawerState) {
+        State state = createShellState();
+        state.ringerModeInternal = ringerMode;
+        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
+        mDialog.onStateChangedH(state);
+
+        mDialog.show(SHOW_REASON_UNKNOWN);
+
+        if (drawerState != RingerDrawerState.INIT) {
+            // in both cases we first open the drawer
+            mDialog.toggleRingerDrawer(true);
+
+            if (drawerState == RingerDrawerState.CLOSE) {
+                mDialog.toggleRingerDrawer(false);
+            }
+        }
+
+        String ringerContainerDescription = mDialog.getSelectedRingerContainerDescription();
+        assumeNotNull(ringerContainerDescription);
+
+        String ringerDescription = mContext.getString(
+                mDialog.getStringDescriptionResourceForRingerMode(ringerMode));
+
+        if (drawerState == RingerDrawerState.OPEN) {
+            assertEquals(ringerDescription, ringerContainerDescription);
+        } else {
+            assertNotSame(ringerDescription, ringerContainerDescription);
+            assertTrue(ringerContainerDescription.startsWith(ringerDescription));
+        }
+    }
+
     @After
     public void teardown() {
         cleanUp(mDialog);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 26a75d0..70d15a0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -119,9 +119,11 @@
         )
     }
 
-    fun sceneInteractor(): SceneInteractor {
+    fun sceneInteractor(
+        repository: SceneContainerRepository = fakeSceneContainerRepository()
+    ): SceneInteractor {
         return SceneInteractor(
-            repository = fakeSceneContainerRepository(),
+            repository = repository,
         )
     }
 
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 3f4f981..23a0782 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -989,21 +989,16 @@
     }
 
     List<AudioPlaybackConfiguration> getActivePlaybackConfigurations(boolean isPrivileged) {
-        synchronized(mPlayers) {
+        synchronized (mPlayerLock) {
             if (isPrivileged) {
                 return new ArrayList<AudioPlaybackConfiguration>(mPlayers.values());
             } else {
-                final List<AudioPlaybackConfiguration> configsPublic;
-                synchronized (mPlayerLock) {
-                    configsPublic = anonymizeForPublicConsumption(
+                return anonymizeForPublicConsumption(
                             new ArrayList<AudioPlaybackConfiguration>(mPlayers.values()));
-                }
-                return configsPublic;
             }
         }
     }
 
-
     /**
      * Inner class to track clients that want to be notified of playback updates
      */
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 61d0afe..fb3f0b3 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -260,7 +260,9 @@
         }
 
         synchronized (mLock) {
-            mProjectionGrant.stop();
+            if (mProjectionGrant != null) {
+                mProjectionGrant.stop();
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 0605345..3ba307b 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -22,6 +22,7 @@
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.admin.DevicePolicyManager;
+import android.app.role.RoleManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
@@ -64,6 +65,8 @@
     private static final int LOCAL_LOG_SIZE = 20;
     private static final String TAG = "BugreportManagerService";
     private static final boolean DEBUG = false;
+    private static final String ROLE_SYSTEM_AUTOMOTIVE_PROJECTION =
+            "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION";
 
     private static final String BUGREPORT_SERVICE = "bugreportd";
     private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000;
@@ -326,11 +329,22 @@
 
         // To gain access through the DUMP permission, the OEM has to allow this package explicitly
         // via sysconfig and privileged permissions.
-        if (mBugreportAllowlistedPackages.contains(callingPackage)
-                && mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
-                        == PackageManager.PERMISSION_GRANTED) {
+        boolean allowlisted = mBugreportAllowlistedPackages.contains(callingPackage);
+        if (!allowlisted) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                allowlisted = mContext.getSystemService(RoleManager.class).getRoleHolders(
+                        ROLE_SYSTEM_AUTOMOTIVE_PROJECTION).contains(callingPackage);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        if (allowlisted && mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.DUMP) == PackageManager.PERMISSION_GRANTED) {
             return;
         }
+
         // For carrier privileges, this can include user-installed apps. This is essentially a
         // function of the current active SIM(s) in the device to let carrier apps through.
         final long token = Binder.clearCallingIdentity();
@@ -346,7 +360,8 @@
 
         String message =
                 callingPackage
-                        + " does not hold the DUMP permission or is not bugreport-whitelisted "
+                        + " does not hold the DUMP permission or is not bugreport-whitelisted or "
+                        + "does not have an allowed role "
                         + (checkCarrierPrivileges ? "and does not have carrier privileges " : "")
                         + "to request a bugreport";
         Slog.w(TAG, message);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 710e0b7..dd434fbe 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -37,6 +37,7 @@
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentProvider;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -1927,11 +1928,32 @@
         }
         if (shortcut.getIcon() != null) {
             ShortcutInfo.validateIcon(shortcut.getIcon());
+            validateIconURI(shortcut);
         }
 
         shortcut.replaceFlags(shortcut.getFlags() & ShortcutInfo.FLAG_LONG_LIVED);
     }
 
+    // Validates the calling process has permission to access shortcut icon's image uri
+    private void validateIconURI(@NonNull final ShortcutInfo si) {
+        final int callingUid = injectBinderCallingUid();
+        final Icon icon = si.getIcon();
+        if (icon == null) {
+            // There's no icon in this shortcut, nothing to validate here.
+            return;
+        }
+        int iconType = icon.getType();
+        if (iconType != Icon.TYPE_URI && iconType != Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+            // The icon is not URI-based, nothing to validate.
+            return;
+        }
+        final Uri uri = icon.getUri();
+        mUriGrantsManagerInternal.checkGrantUriPermission(callingUid, si.getPackage(),
+                ContentProvider.getUriWithoutUserId(uri),
+                Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(callingUid)));
+    }
+
     private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
         fixUpIncomingShortcutInfo(shortcut, forUpdate, /*forPinRequest=*/ false);
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e93f358..62273b5 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -226,6 +226,7 @@
 import static com.android.server.wm.LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
 import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
 import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
@@ -2676,7 +2677,9 @@
     private boolean transferSplashScreenIfNeeded() {
         if (finishing || !mHandleExitSplashScreen || mStartingSurface == null
                 || mStartingWindow == null
-                || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH) {
+                || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH
+                // skip copy splash screen to client if it was resized
+                || (mStartingData != null && mStartingData.mResizedFromTransfer)) {
             return false;
         }
         if (isTransferringSplashScreen()) {
@@ -7632,7 +7635,8 @@
     @Override
     void prepareSurfaces() {
         final boolean show = isVisible() || isAnimating(PARENTS,
-                ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
+                ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS
+                        | ANIMATION_TYPE_PREDICT_BACK);
 
         if (mSurfaceControl != null) {
             if (show && !mLastSurfaceShowing) {
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 0c196d7..976641b 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -26,7 +26,9 @@
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
 import static com.android.server.wm.BackNavigationProto.ANIMATION_IN_PROGRESS;
+import static com.android.server.wm.BackNavigationProto.ANIMATION_RUNNING;
 import static com.android.server.wm.BackNavigationProto.LAST_BACK_TYPE;
+import static com.android.server.wm.BackNavigationProto.MAIN_OPEN_ACTIVITY;
 import static com.android.server.wm.BackNavigationProto.SHOW_WALLPAPER;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
 
@@ -50,6 +52,7 @@
 import android.window.BackAnimationAdapter;
 import android.window.BackNavigationInfo;
 import android.window.IBackAnimationFinishedCallback;
+import android.window.IWindowlessStartingSurfaceCallback;
 import android.window.OnBackInvokedCallbackInfo;
 import android.window.TaskSnapshot;
 
@@ -73,6 +76,8 @@
     private @BackNavigationInfo.BackTargetType int mLastBackType;
     private boolean mShowWallpaper;
     private Runnable mPendingAnimation;
+
+    private boolean mBackAnimationRunning;
     private final NavigationMonitor mNavigationMonitor = new NavigationMonitor();
 
     private AnimationHandler mAnimationHandler;
@@ -474,7 +479,7 @@
                 final ActivityRecord ar = openApps.valueAt(i);
                 if (mAnimationHandler.isTarget(ar, true /* open */)) {
                     openApps.removeAt(i);
-                    mAnimationHandler.mOpenTransitionTargetMatch = true;
+                    mAnimationHandler.markStartingSurfaceMatch();
                 }
             }
             for (int i = closeApps.size() - 1; i >= 0; --i) {
@@ -583,8 +588,9 @@
      * The closing target should only exist in close list, but the opening target can be either in
      * open or close list.
      */
-    void onTransactionReady(Transition transition, ArrayList<Transition.ChangeInfo> targets) {
-        if (!isMonitoringTransition()) {
+    void onTransactionReady(Transition transition, ArrayList<Transition.ChangeInfo> targets,
+            SurfaceControl.Transaction startTransaction) {
+        if (!isMonitoringTransition() || targets.isEmpty()) {
             return;
         }
         for (int i = targets.size() - 1; i >= 0; --i) {
@@ -613,6 +619,17 @@
                 Slog.e(TAG, "Gesture animation is applied on another transition?");
             }
             mWaitTransitionFinish = transition;
+            // Flag target matches to defer remove the splash screen.
+            for (int i = mTmpOpenApps.size() - 1; i >= 0; --i) {
+                final WindowContainer wc = mTmpOpenApps.get(i);
+                if (mAnimationHandler.isTarget(wc, true /* open */)) {
+                    mAnimationHandler.markStartingSurfaceMatch();
+                    break;
+                }
+            }
+            // Because the target will reparent to transition root, so it cannot be controlled by
+            // animation leash. Hide the close target when transition starts.
+            startTransaction.hide(mAnimationHandler.mCloseAdaptor.mTarget.getSurfaceControl());
         }
         mTmpOpenApps.clear();
         mTmpCloseApps.clear();
@@ -633,6 +650,7 @@
         mAnimationHandler.clearBackAnimateTarget();
         mNavigationMonitor.stopMonitorTransition();
         mWaitTransitionFinish = null;
+        mBackAnimationRunning = false;
     }
 
     /**
@@ -717,11 +735,7 @@
         // This will be set before transition happen, to know whether the real opening target
         // exactly match animating target. When target match, reparent the starting surface to
         // the opening target like starting window do.
-        private boolean mOpenTransitionTargetMatch;
-        // The starting surface task Id. Used to clear the starting surface if the animation has
-        // request one during animating.
-        private int mRequestedStartingSurfaceTaskId;
-        private SurfaceControl mStartingSurface;
+        private boolean mStartingSurfaceTargetMatch;
         private ActivityRecord mOpenActivity;
 
         AnimationHandler(WindowManagerService wms) {
@@ -765,8 +779,8 @@
                 return;
             }
 
-            mCloseAdaptor = createAdaptor(closeTarget, false /* isOpen */);
-            mOpenAdaptor = createAdaptor(open, true /* isOpen */);
+            mCloseAdaptor = createAdaptor(closeTarget, false, mSwitchType);
+            mOpenAdaptor = createAdaptor(open, true, mSwitchType);
             mOpenActivity = openActivity;
             if (mCloseAdaptor.mAnimationTarget == null || mOpenAdaptor.mAnimationTarget == null) {
                 Slog.w(TAG, "composeNewAnimations fail, skip");
@@ -774,8 +788,8 @@
             }
         }
 
-        boolean composeAnimations(@NonNull WindowContainer close, @NonNull WindowContainer open,
-                ActivityRecord openActivity) {
+        private boolean composeAnimations(@NonNull WindowContainer close,
+                @NonNull WindowContainer open, ActivityRecord openActivity) {
             if (mComposed || mWaitTransition) {
                 Slog.e(TAG, "Previous animation is running " + this);
                 return false;
@@ -805,28 +819,6 @@
                     .isSupportWindowlessStartingSurface();
         }
 
-        void createStartingSurface(TaskSnapshot snapshot) {
-            if (!mComposed) {
-                return;
-            }
-
-            final ActivityRecord topActivity = getTopOpenActivity();
-            if (topActivity == null) {
-                Slog.e(TAG, "createStartingSurface fail, no open activity: " + this);
-                return;
-            }
-            // TODO (b/257857570) draw snapshot by starting surface.
-        }
-
-        private ActivityRecord getTopOpenActivity() {
-            if (mSwitchType == ACTIVITY_SWITCH) {
-                return mOpenAdaptor.mTarget.asActivityRecord();
-            } else if (mSwitchType == TASK_SWITCH) {
-                return mOpenAdaptor.mTarget.asTask().getTopNonFinishingActivity();
-            }
-            return null;
-        }
-
         boolean containTarget(ArrayList<WindowContainer> wcs, boolean open) {
             for (int i = wcs.size() - 1; i >= 0; --i) {
                 if (isTarget(wcs.get(i), open)) {
@@ -860,13 +852,13 @@
             if (!mComposed) {
                 return;
             }
-            cleanUpWindowlessSurface();
 
             if (mCloseAdaptor != null) {
                 mCloseAdaptor.mTarget.cancelAnimation();
                 mCloseAdaptor = null;
             }
             if (mOpenAdaptor != null) {
+                mOpenAdaptor.cleanUpWindowlessSurface(mStartingSurfaceTargetMatch);
                 mOpenAdaptor.mTarget.cancelAnimation();
                 mOpenAdaptor = null;
             }
@@ -875,36 +867,16 @@
             }
         }
 
-        private void cleanUpWindowlessSurface() {
-            final ActivityRecord ar = getTopOpenActivity();
-            if (ar == null) {
-                Slog.w(TAG, "finishPresentAnimations without top activity: " + this);
-            }
-            final SurfaceControl.Transaction pendingT = ar != null ? ar.getPendingTransaction()
-                    : mOpenAdaptor.mTarget.getPendingTransaction();
-            // ensure open target is visible before cancel animation.
-            mOpenTransitionTargetMatch &= ar != null;
-            if (mOpenTransitionTargetMatch) {
-                pendingT.show(ar.getSurfaceControl());
-            }
-            if (mRequestedStartingSurfaceTaskId != 0) {
-                // If open target match, reparent to open activity
-                if (mStartingSurface != null && mOpenTransitionTargetMatch) {
-                    pendingT.reparent(mStartingSurface, ar.getSurfaceControl());
-                }
-                // remove starting surface.
-                mStartingSurface = null;
-                // TODO (b/257857570) draw snapshot by starting surface.
-                mRequestedStartingSurfaceTaskId = 0;
-            }
+        void markStartingSurfaceMatch() {
+            mStartingSurfaceTargetMatch = true;
+            mOpenAdaptor.reparentWindowlessSurfaceToTarget();
         }
 
         void clearBackAnimateTarget() {
             finishPresentAnimations();
             mComposed = false;
             mWaitTransition = false;
-            mOpenTransitionTargetMatch = false;
-            mRequestedStartingSurfaceTaskId = 0;
+            mStartingSurfaceTargetMatch = false;
             mSwitchType = UNKNOWN;
             mOpenActivity = null;
         }
@@ -935,9 +907,9 @@
         }
 
         private static BackWindowAnimationAdaptor createAdaptor(
-                WindowContainer target, boolean isOpen) {
+                WindowContainer target, boolean isOpen, int switchType) {
             final BackWindowAnimationAdaptor adaptor =
-                    new BackWindowAnimationAdaptor(target, isOpen);
+                    new BackWindowAnimationAdaptor(target, isOpen, switchType);
             final SurfaceControl.Transaction pt = target.getPendingTransaction();
             target.startAnimation(pt, adaptor, false /* hidden */, ANIMATION_TYPE_PREDICT_BACK);
             // Workaround to show TaskFragment which can be hide in Transitions and won't show
@@ -957,11 +929,19 @@
             private final WindowContainer mTarget;
             private final boolean mIsOpen;
             private RemoteAnimationTarget mAnimationTarget;
+            private final int mSwitchType;
 
-            BackWindowAnimationAdaptor(WindowContainer closeTarget, boolean isOpen) {
-                mBounds.set(closeTarget.getBounds());
-                mTarget = closeTarget;
+            // The starting surface task Id. Used to clear the starting surface if the animation has
+            // requested one during animating.
+            private int mRequestedStartingSurfaceId = INVALID_TASK_ID;
+            private SurfaceControl mStartingSurface;
+
+            BackWindowAnimationAdaptor(WindowContainer target, boolean isOpen,
+                    int switchType) {
+                mBounds.set(target.getBounds());
+                mTarget = target;
                 mIsOpen = isOpen;
+                mSwitchType = switchType;
             }
             @Override
             public boolean getShowWallpaper() {
@@ -979,6 +959,8 @@
             public void onAnimationCancelled(SurfaceControl animationLeash) {
                 if (mCapturedLeash == animationLeash) {
                     mCapturedLeash = null;
+                    mRequestedStartingSurfaceId = INVALID_TASK_ID;
+                    mStartingSurface = null;
                 }
             }
 
@@ -1009,8 +991,15 @@
                     return mAnimationTarget;
                 }
                 Task t = mTarget.asTask();
-                final ActivityRecord r = t != null ? t.getTopNonFinishingActivity()
-                        : mTarget.asActivityRecord();
+                ActivityRecord r = null;
+                if (t == null && mTarget.asTaskFragment() != null) {
+                    t = mTarget.asTaskFragment().getTask();
+                    r = mTarget.asTaskFragment().getTopNonFinishingActivity();
+                }
+                if (r == null) {
+                    r = t != null ? t.getTopNonFinishingActivity()
+                            : mTarget.asActivityRecord();
+                }
                 if (t == null && r != null) {
                     t = r.getTask();
                 }
@@ -1037,6 +1026,77 @@
                         r.checkEnterPictureInPictureAppOpsState());
                 return mAnimationTarget;
             }
+
+            void createStartingSurface() {
+                if (!mIsOpen) {
+                    return;
+                }
+                final Task openTask = mSwitchType == TASK_SWITCH
+                        ? mTarget.asTask() : mSwitchType == ACTIVITY_SWITCH
+                        ? mTarget.asActivityRecord().getTask() : null;
+                if (openTask == null) {
+                    return;
+                }
+                final ActivityRecord mainActivity = mSwitchType == ACTIVITY_SWITCH
+                        ? mTarget.asActivityRecord()
+                        : openTask.getTopNonFinishingActivity();
+                if (mainActivity == null) {
+                    return;
+                }
+                final TaskSnapshot snapshot = getSnapshot(mTarget);
+                mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController
+                        .addWindowlessStartingSurface(openTask, mainActivity,
+                                mAnimationTarget.leash, snapshot,
+                                new IWindowlessStartingSurfaceCallback.Stub() {
+                            // Once the starting surface has been created in shell, it will call
+                            // onSurfaceAdded to pass the created surface to core, so if a
+                            // transition is triggered by the back gesture, there doesn't need to
+                            // create another starting surface for the opening target, just reparent
+                            // the starting surface to the opening target.
+                            // Note if cleanUpWindowlessSurface happen prior than onSurfaceAdded
+                            // called, there won't be able to reparent the starting surface on
+                            // opening target. But if that happens and transition target is matched,
+                            // the app window should already draw.
+                                    @Override
+                                    public void onSurfaceAdded(SurfaceControl sc) {
+                                        synchronized (mTarget.mWmService.mGlobalLock) {
+                                            if (mRequestedStartingSurfaceId != INVALID_TASK_ID) {
+                                                mStartingSurface = sc;
+                                            }
+                                        }
+                                    }
+                                });
+            }
+
+            // When back gesture has triggered and transition target matches navigation target,
+            // reparent the starting surface to the opening target as it's starting window.
+            void reparentWindowlessSurfaceToTarget() {
+                if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
+                    return;
+                }
+                // If open target matches, reparent to open activity or task
+                if (mStartingSurface != null && mStartingSurface.isValid()) {
+                    mTarget.getPendingTransaction()
+                            .reparent(mStartingSurface, mTarget.getSurfaceControl());
+                    // remove starting surface.
+                    mStartingSurface = null;
+                }
+            }
+
+            /**
+             * Ask shell to clear the starting surface.
+             * @param openTransitionMatch if true, shell will play the remove starting window
+             *                            animation, otherwise remove it directly.
+             */
+            void cleanUpWindowlessSurface(boolean openTransitionMatch) {
+                if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
+                    return;
+                }
+                mTarget.mWmService.mAtmService.mTaskOrganizerController
+                        .removeWindowlessStartingSurface(mRequestedStartingSurfaceId,
+                                !openTransitionMatch);
+                mRequestedStartingSurfaceId = INVALID_TASK_ID;
+            }
         }
 
         ScheduleAnimationBuilder prepareAnimation(int backType, BackAnimationAdapter adapter,
@@ -1089,15 +1149,13 @@
 
             /**
              * Apply preview strategy on the opening target
-             * @param open The opening target.
+             * @param openAnimationAdaptor The animator who can create starting surface.
              * @param visibleOpenActivity  The visible activity in opening target.
-             * @return If the preview strategy is launch behind, returns the Activity that has
-             *         launchBehind set, or null otherwise.
              */
-            private void applyPreviewStrategy(WindowContainer open,
+            private void applyPreviewStrategy(BackWindowAnimationAdaptor openAnimationAdaptor,
                     ActivityRecord visibleOpenActivity) {
                 if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) {
-                    createStartingSurface(getSnapshot(open));
+                    openAnimationAdaptor.createStartingSurface();
                     return;
                 }
                 setLaunchBehind(visibleOpenActivity);
@@ -1119,7 +1177,7 @@
                 if (!composeAnimations(mCloseTarget, mOpenTarget, openActivity)) {
                     return null;
                 }
-                applyPreviewStrategy(mOpenTarget, openActivity);
+                applyPreviewStrategy(mOpenAdaptor, openActivity);
 
                 final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback();
                 final RemoteAnimationTarget[] targets = getAnimationTargets();
@@ -1220,6 +1278,7 @@
         if (mPendingAnimation != null) {
             mPendingAnimation.run();
             mPendingAnimation = null;
+            mBackAnimationRunning = true;
         }
     }
 
@@ -1236,9 +1295,6 @@
     }
 
     static TaskSnapshot getSnapshot(@NonNull WindowContainer w) {
-        if (!isScreenshotEnabled()) {
-            return null;
-        }
         if (w.asTask() != null) {
             final Task task = w.asTask();
             return  task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot(
@@ -1247,8 +1303,8 @@
         }
 
         if (w.asActivityRecord() != null) {
-            // TODO (b/259497289) return TaskSnapshot when feature complete.
-            return null;
+            final ActivityRecord ar = w.asActivityRecord();
+            return ar.mWmService.mSnapshotController.mActivitySnapshotController.getSnapshot(ar);
         }
         return null;
     }
@@ -1270,6 +1326,12 @@
         proto.write(ANIMATION_IN_PROGRESS, mBackAnimationInProgress);
         proto.write(LAST_BACK_TYPE, mLastBackType);
         proto.write(SHOW_WALLPAPER, mShowWallpaper);
+        if (mAnimationHandler.mOpenActivity != null) {
+            mAnimationHandler.mOpenActivity.writeNameToProto(proto, MAIN_OPEN_ACTIVITY);
+        } else {
+            proto.write(MAIN_OPEN_ACTIVITY, "");
+        }
+        proto.write(ANIMATION_RUNNING, mBackAnimationRunning);
         proto.end(token);
     }
 }
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index cff86ad..2b22d75 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -38,6 +38,10 @@
      */
     Task mAssociatedTask;
 
+
+    /** Whether the starting window is resized from transfer across activities. */
+    boolean mResizedFromTransfer;
+
     /** Whether the starting window is drawn. */
     boolean mIsDisplayed;
 
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index cdb4ad6..b72d027 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -790,12 +790,8 @@
     }
 
     boolean isSupportWindowlessStartingSurface() {
-        // Enable after ag/20426257
         final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
-        if (lastOrganizer == null) {
-            return false;
-        }
-        return false;
+        return lastOrganizer != null;
     }
     /**
      * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index b7c8092..f33af5e 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1499,7 +1499,8 @@
         mTargets = calculateTargets(mParticipants, mChanges);
 
         // Check whether the participants were animated from back navigation.
-        mController.mAtm.mBackNavigationController.onTransactionReady(this, mTargets);
+        mController.mAtm.mBackNavigationController.onTransactionReady(this, mTargets,
+                transaction);
         final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction);
         info.setDebugId(mSyncId);
         mController.assignTrack(this, info);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 140255b..b2a2452 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1935,7 +1935,9 @@
         }
         final ActivityRecord atoken = mActivityRecord;
         if (atoken != null) {
-            return ((!isParentWindowHidden() && atoken.isVisible())
+            final boolean isVisible = isStartingWindowAssociatedToTask()
+                    ? mStartingData.mAssociatedTask.isVisible() : atoken.isVisible();
+            return ((!isParentWindowHidden() && isVisible)
                     || isAnimationRunningSelfOrParent());
         }
         final WallpaperWindowToken wtoken = mToken.asWallpaperToken();
@@ -2330,6 +2332,13 @@
             // IME surface association. (e.g. Attach IME surface on the display instead of the
             // app when the app bounds being letterboxed.)
             mDisplayContent.updateImeControlTarget(isImeLayeringTarget() /* updateImeParent */);
+            // Fix the starting window to task when Activity has changed.
+            if (mStartingData != null && mStartingData.mAssociatedTask == null
+                    && !mTempConfiguration.windowConfiguration.getBounds().equals(getBounds())) {
+                mStartingData.mResizedFromTransfer = true;
+                // Lock the starting window to task, so it won't resize from transfer anymore.
+                mActivityRecord.associateStartingWindowWithTaskIfNeeded();
+            }
         }
     }
 
@@ -3902,7 +3911,7 @@
      * LetterboxUiController#shouldShowLetterboxUi} for more context.
      */
     boolean areAppWindowBoundsLetterboxed() {
-        return mActivityRecord != null
+        return mActivityRecord != null && !isStartingWindowAssociatedToTask()
                 && (mActivityRecord.areBoundsLetterboxed() || isLetterboxedForDisplayCutout());
     }
 
@@ -5668,6 +5677,12 @@
             // TODO(b/233286785): Add sync support to wallpaper.
             return true;
         }
+        if (mActivityRecord != null && mViewVisibility != View.VISIBLE
+                && mWinAnimator.mAttrType != TYPE_BASE_APPLICATION
+                && mWinAnimator.mAttrType != TYPE_APPLICATION_STARTING) {
+            // Skip sync for invisible app windows which are not managed by activity lifecycle.
+            return false;
+        }
         // In the WindowContainer implementation we immediately mark ready
         // since a generic WindowContainer only needs to wait for its
         // children to finish and is immediately ready from its own
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index f660b42..fe979b6 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -418,14 +418,11 @@
             setDeviceServer(server);
         }
 
-        @RequiresPermission(anyOf = {Manifest.permission.QUERY_USERS,
-                Manifest.permission.CREATE_USERS,
-                Manifest.permission.MANAGE_USERS})
         public Device(BluetoothDevice bluetoothDevice) {
             mBluetoothDevice = bluetoothDevice;
             mServiceInfo = null;
             mUid = mBluetoothServiceUid;
-            mUserId = mUserManager.getMainUser().getIdentifier();
+            mUserId = UserHandle.getUserId(mUid);
         }
 
         private void setDeviceServer(IMidiDeviceServer server) {
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 35b9bc3..4a8d73d2 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -254,12 +254,45 @@
             }
             final long identity = Binder.clearCallingIdentity();
             try {
-                return userState.getCustomPrinterIcon(printerId);
+                Icon icon = userState.getCustomPrinterIcon(printerId);
+                return validateIconUserBoundary(icon);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
         }
 
+        /**
+         * Validates the custom printer icon to see if it's not in the calling user space.
+         * If the condition is not met, return null. Otherwise, return the original icon.
+         *
+         * @param icon
+         * @return icon (validated)
+         */
+        private Icon validateIconUserBoundary(Icon icon) {
+            // Refer to Icon#getUriString for context. The URI string is invalid for icons of
+            // incompatible types.
+            if (icon != null && (icon.getType() == Icon.TYPE_URI
+                    || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
+                String encodedUser = icon.getUri().getEncodedUserInfo();
+
+                // If there is no encoded user, the URI is calling into the calling user space
+                if (encodedUser != null) {
+                    int userId = Integer.parseInt(encodedUser);
+                    // resolve encoded user
+                    final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+
+                    synchronized (mLock) {
+                        // Only the current group members can get the printer icons.
+                        if (resolveCallingProfileParentLocked(resolvedUserId)
+                                != getCurrentUserId()) {
+                            return null;
+                        }
+                    }
+                }
+            }
+            return icon;
+        }
+
         @Override
         public void cancelPrintJob(PrintJobId printJobId, int appId, int userId) {
             if (printJobId == null) {
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 107dde2..fa0a971 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -110,6 +110,7 @@
     <uses-permission android:name="android.permission.ACCESS_CONTEXT_HUB" />
     <uses-permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" />
     <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION" />
+    <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
 
     <queries>
         <package android:name="com.android.servicestests.apps.suspendtestapp" />
diff --git a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
index 52c6777..24029b1 100644
--- a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
@@ -16,15 +16,18 @@
 
 package com.android.server.os;
 
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertThrows;
 
+import android.app.role.RoleManager;
 import android.content.Context;
 import android.os.Binder;
 import android.os.BugreportManager.BugreportCallback;
 import android.os.IBinder;
 import android.os.IDumpstateListener;
+import android.os.Process;
 import android.os.RemoteException;
 import android.util.ArraySet;
 import android.util.Pair;
@@ -37,21 +40,23 @@
 import org.junit.runner.RunWith;
 
 import java.io.FileDescriptor;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 
 @RunWith(AndroidJUnit4.class)
 public class BugreportManagerServiceImplTest {
 
-    Context mContext;
-    BugreportManagerServiceImpl mService;
-    BugreportManagerServiceImpl.BugreportFileManager mBugreportFileManager;
+    private Context mContext;
+    private BugreportManagerServiceImpl mService;
+    private BugreportManagerServiceImpl.BugreportFileManager mBugreportFileManager;
 
-    int mCallingUid = 1234;
-    String mCallingPackage  = "test.package";
+    private int mCallingUid = 1234;
+    private String mCallingPackage  = "test.package";
 
-    String mBugreportFile = "bugreport-file.zip";
-    String mBugreportFile2 = "bugreport-file2.zip";
+    private String mBugreportFile = "bugreport-file.zip";
+    private String mBugreportFile2 = "bugreport-file2.zip";
 
     @Before
     public void setUp() {
@@ -109,6 +114,36 @@
                 BugreportCallback.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE);
     }
 
+    @Test
+    public void testCancelBugreportWithoutRole() throws Exception {
+        // Clear out allowlisted packages.
+        mService = new BugreportManagerServiceImpl(
+                new BugreportManagerServiceImpl.Injector(mContext, new ArraySet<>()));
+
+        assertThrows(SecurityException.class, () -> mService.cancelBugreport(
+                Binder.getCallingUid(), mContext.getPackageName()));
+    }
+
+    @Test
+    public void testCancelBugreportWithRole() throws Exception {
+        // Clear out allowlisted packages.
+        mService = new BugreportManagerServiceImpl(
+                new BugreportManagerServiceImpl.Injector(mContext, new ArraySet<>()));
+        RoleManager roleManager = mContext.getSystemService(RoleManager.class);
+        CallbackFuture future = new CallbackFuture();
+        runWithShellPermissionIdentity(() -> roleManager.setBypassingRoleQualification(true));
+        runWithShellPermissionIdentity(() -> roleManager.addRoleHolderAsUser(
+                "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION",
+                mContext.getPackageName(),
+                /* flags= */ 0,
+                Process.myUserHandle(),
+                mContext.getMainExecutor(),
+                future));
+
+        assertThat(future.get()).isEqualTo(true);
+        mService.cancelBugreport(Binder.getCallingUid(), mContext.getPackageName());
+    }
+
     private static class Listener implements IDumpstateListener {
         CountDownLatch mLatch;
         int mErrorCode;
@@ -149,4 +184,12 @@
             return mErrorCode;
         }
     }
+
+    private static class CallbackFuture extends CompletableFuture<Boolean>
+            implements Consumer<Boolean> {
+        @Override
+        public void accept(Boolean successful) {
+            complete(successful);
+        }
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 6d7f2c1..1c86758 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -570,6 +570,7 @@
         final ContextWrapper contextSpy = Mockito.spy(new ContextWrapper(mWm.mContext));
         final Resources resourcesSpy = Mockito.spy(contextSpy.getResources());
 
+        spyOn(mAtm.mTaskOrganizerController);
         when(contextSpy.getResources()).thenReturn(resourcesSpy);
 
         MockitoSession mockitoSession = mockitoSession().mockStatic(BackNavigationController.class)
@@ -597,7 +598,8 @@
                         mBackAnimationAdapter, task, mRootHomeTask, bottomActivity, homeActivity);
         assertTrue(toHomeBuilder.mIsLaunchBehind);
         toHomeBuilder.build();
-        verify(animationHandler, never()).createStartingSurface(any());
+        verify(mAtm.mTaskOrganizerController, never())
+                .addWindowlessStartingSurface(any(), any(), any(), any(), any());
         animationHandler.clearBackAnimateTarget();
 
         // Back to ACTIVITY and TASK have the same logic, just with different target.
@@ -609,9 +611,11 @@
         assertFalse(toActivityBuilder.mIsLaunchBehind);
         toActivityBuilder.build();
         if (preferWindowlessSurface) {
-            verify(animationHandler).createStartingSurface(any());
+            verify(mAtm.mTaskOrganizerController)
+                    .addWindowlessStartingSurface(any(), any(), any(), any(), any());
         } else {
-            verify(animationHandler, never()).createStartingSurface(any());
+            verify(mAtm.mTaskOrganizerController, never())
+                    .addWindowlessStartingSurface(any(), any(), any(), any(), any());
         }
     }
 
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index feef049..d41c019 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -81,6 +81,19 @@
             mCallback = callback;
         }
 
+        @Override
+        public void onServiceConnected() {
+            if (mCallback != null) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mCallback.onServiceConnected());
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
         public void onHotspotNetworksUpdated(@NonNull List<HotspotNetwork> networks) {
             if (mCallback != null) {
                 final long token = Binder.clearCallingIdentity();
@@ -117,6 +130,7 @@
             }
         }
 
+        @Override
         public void onHotspotNetworkConnectionStatusChanged(
                 @NonNull HotspotNetworkConnectionStatus status) {
             if (mCallback != null) {
@@ -251,7 +265,6 @@
             synchronized (mProxyDataLock) {
                 mProxyMap.put(callback, proxy);
             }
-            callback.onServiceConnected();
         } catch (RemoteException e) {
             Log.e(TAG, "Exception in registerCallback", e);
             callback.onRegisterCallbackFailed(e);
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
index 737aa6d..521f943 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
@@ -31,4 +31,5 @@
     oneway void onKnownNetworksUpdated(in List<KnownNetwork> networks);
     oneway void onKnownNetworkConnectionStatusChanged(in KnownNetworkConnectionStatus status);
     oneway void onSharedConnectivitySettingsChanged(in SharedConnectivitySettingsState state);
+    oneway void onServiceConnected();
 }
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index 2bbe919..ebda6f1 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -276,6 +276,11 @@
 
     private void onRegisterCallback(ISharedConnectivityCallback callback) {
         mRemoteCallbackList.register(callback);
+        try {
+            callback.onServiceConnected();
+        } catch (RemoteException e) {
+            if (DEBUG) Log.w(TAG, "Exception in onRegisterCallback", e);
+        }
         if (mCountDownLatch != null) {
             mCountDownLatch.countDown();
         }