Merge "Reset blur radius to 0 whenever blur is not supported" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 050cad4..d4ed533 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -2419,6 +2419,7 @@
     field public static final int accessibilityActionScrollRight = 16908347; // 0x102003b
     field public static final int accessibilityActionScrollToPosition = 16908343; // 0x1020037
     field public static final int accessibilityActionScrollUp = 16908344; // 0x1020038
+    field @FlaggedApi("android.view.accessibility.a11y_selection_api") public static final int accessibilityActionSetExtendedSelection;
     field public static final int accessibilityActionSetProgress = 16908349; // 0x102003d
     field public static final int accessibilityActionShowOnScreen = 16908342; // 0x1020036
     field public static final int accessibilityActionShowTextSuggestions = 16908376; // 0x1020058
@@ -56266,6 +56267,7 @@
     method public android.view.accessibility.AccessibilityNodeInfo getParent();
     method @Nullable public android.view.accessibility.AccessibilityNodeInfo getParent(int);
     method public android.view.accessibility.AccessibilityNodeInfo.RangeInfo getRangeInfo();
+    method @FlaggedApi("android.view.accessibility.a11y_selection_api") @Nullable public android.view.accessibility.AccessibilityNodeInfo.Selection getSelection();
     method @Nullable public CharSequence getStateDescription();
     method @FlaggedApi("android.view.accessibility.supplemental_description") @Nullable public CharSequence getSupplementalDescription();
     method public CharSequence getText();
@@ -56374,6 +56376,7 @@
     method public void setScreenReaderFocusable(boolean);
     method public void setScrollable(boolean);
     method public void setSelected(boolean);
+    method @FlaggedApi("android.view.accessibility.a11y_selection_api") public void setSelection(@Nullable android.view.accessibility.AccessibilityNodeInfo.Selection);
     method public void setShowingHintText(boolean);
     method public void setSource(android.view.View);
     method public void setSource(android.view.View, int);
@@ -56406,6 +56409,7 @@
     field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
     field @FlaggedApi("android.view.accessibility.granular_scrolling") public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT = "android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
     field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
+    field @FlaggedApi("android.view.accessibility.a11y_selection_api") public static final String ACTION_ARGUMENT_SELECTION_PARCELABLE = "android.view.accessibility.action.ARGUMENT_SELECTION_PARCELABLE";
     field public static final String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
     field public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
     field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
@@ -56503,6 +56507,7 @@
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_TO_POSITION;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_UP;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SELECT;
+    field @FlaggedApi("android.view.accessibility.a11y_selection_api") @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_EXTENDED_SELECTION;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_PROGRESS;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_SELECTION;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_TEXT;
@@ -56588,6 +56593,26 @@
     field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
   }
 
+  @FlaggedApi("android.view.accessibility.a11y_selection_api") public static final class AccessibilityNodeInfo.Selection implements android.os.Parcelable {
+    ctor public AccessibilityNodeInfo.Selection(@NonNull android.view.accessibility.AccessibilityNodeInfo.SelectionPosition, @NonNull android.view.accessibility.AccessibilityNodeInfo.SelectionPosition);
+    method public int describeContents();
+    method @NonNull public android.view.accessibility.AccessibilityNodeInfo.SelectionPosition getEnd();
+    method @NonNull public android.view.accessibility.AccessibilityNodeInfo.SelectionPosition getStart();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo.Selection> CREATOR;
+  }
+
+  @FlaggedApi("android.view.accessibility.a11y_selection_api") public static final class AccessibilityNodeInfo.SelectionPosition implements android.os.Parcelable {
+    ctor public AccessibilityNodeInfo.SelectionPosition(@NonNull android.view.accessibility.AccessibilityNodeInfo, int);
+    ctor public AccessibilityNodeInfo.SelectionPosition(@NonNull android.view.View, int);
+    ctor public AccessibilityNodeInfo.SelectionPosition(@NonNull android.view.View, int, int);
+    method public int describeContents();
+    method @Nullable public android.view.accessibility.AccessibilityNodeInfo getNode();
+    method public int getOffset();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo.SelectionPosition> CREATOR;
+  }
+
   public static final class AccessibilityNodeInfo.TouchDelegateInfo implements android.os.Parcelable {
     ctor public AccessibilityNodeInfo.TouchDelegateInfo(@NonNull java.util.Map<android.graphics.Region,android.view.View>);
     method public int describeContents();
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index e71dffa..577113b 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -1,3 +1,4 @@
+
 // Baseline format: 1.0
 BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
     Field 'ACTION_NEXT_ALARM_CLOCK_CHANGED' is missing @BroadcastBehavior
@@ -243,8 +244,6 @@
     Field 'ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED' is missing @BroadcastBehavior
 BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE:
     Field 'ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE' is missing @BroadcastBehavior
-
-
 DeprecationMismatch: android.accounts.AccountManager#newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle):
     Method android.accounts.AccountManager.newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
 DeprecationMismatch: android.app.Activity#enterPictureInPictureMode():
@@ -381,8 +380,6 @@
     Method android.webkit.WebViewDatabase.hasFormData(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
 DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]):
     Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
-
-
 FlaggedApiLiteral: android.Manifest.permission#BIND_APP_FUNCTION_SERVICE:
     @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER).
 FlaggedApiLiteral: android.Manifest.permission#BIND_TV_AD_SERVICE:
@@ -405,26 +402,22 @@
     @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.content.pm.Flags.FLAG_SDK_LIB_INDEPENDENCE).
 FlaggedApiLiteral: android.R.attr#supplementalDescription:
     @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_SUPPLEMENTAL_DESCRIPTION).
+FlaggedApiLiteral: android.R.id#accessibilityActionSetExtendedSelection:
+    @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_A11Y_SELECTION_API).
 FlaggedApiLiteral: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INTERNAL_ERROR:
     @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS).
 FlaggedApiLiteral: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INVALID:
     @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS).
 FlaggedApiLiteral: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_SUCCESS:
     @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS).
-
-
 InvalidNullabilityOverride: android.app.Notification.TvExtender#extend(android.app.Notification.Builder) parameter #0:
     Invalid nullability on parameter `builder` in method `extend`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
 InvalidNullabilityOverride: android.media.midi.MidiUmpDeviceService#onBind(android.content.Intent) parameter #0:
     Invalid nullability on parameter `intent` in method `onBind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-
-
 KotlinOperator: android.graphics.Matrix44#get(int, int):
     Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
 KotlinOperator: android.graphics.Matrix44#set(int, int, float):
     Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
-
-
 MissingGetterMatchingBuilder: android.os.RemoteCallbackList.Builder#setInterfaceDiedCallback(android.os.RemoteCallbackList.Builder.InterfaceDiedCallback<E>):
     android.os.RemoteCallbackList does not declare a `getInterfaceDiedCallback()` method matching method android.os.RemoteCallbackList.Builder.setInterfaceDiedCallback(android.os.RemoteCallbackList.Builder.InterfaceDiedCallback<E>)
 RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
@@ -1117,14 +1110,10 @@
     Method 'setBlockNetworkLoads' documentation mentions permissions without declaring @RequiresPermission
 RequiresPermission: android.webkit.WebSettings#setGeolocationEnabled(boolean):
     Method 'setGeolocationEnabled' documentation mentions permissions without declaring @RequiresPermission
-
-
 Todo: android.hardware.camera2.params.StreamConfigurationMap:
     Documentation mentions 'TODO'
 Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.database.Cursor):
     Documentation mentions 'TODO'
-
-
 UnflaggedApi: android.R.color#on_surface_disabled_material:
     New API must be flagged with @FlaggedApi: field android.R.color.on_surface_disabled_material
 UnflaggedApi: android.R.color#outline_disabled_material:
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3633b4e..0ca4a32 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -11204,10 +11204,15 @@
      * </pre>
      *
      *
-     *
+     * <p>
      * NOTE: The progress bar layout will be mirrored for RTL layout.
+     * </p>
+     *
+     * <p>
      * NOTE: The extras set by {@link Notification.Builder#setProgress} will be overridden by
-     * the values set on this style object when the notification is built.
+     * the values set on this style object when the notification is built.  Therefore, that method
+     * is not used with this style.
+     * </p>
      *
      */
     @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
diff --git a/media/java/android/media/Image.java b/core/java/android/media/Image.java
similarity index 100%
rename from media/java/android/media/Image.java
rename to core/java/android/media/Image.java
diff --git a/media/java/android/media/ImageReader.java b/core/java/android/media/ImageReader.java
similarity index 100%
rename from media/java/android/media/ImageReader.java
rename to core/java/android/media/ImageReader.java
diff --git a/media/java/android/media/ImageUtils.java b/core/java/android/media/ImageUtils.java
similarity index 100%
rename from media/java/android/media/ImageUtils.java
rename to core/java/android/media/ImageUtils.java
diff --git a/media/java/android/media/ImageWriter.java b/core/java/android/media/ImageWriter.java
similarity index 100%
rename from media/java/android/media/ImageWriter.java
rename to core/java/android/media/ImageWriter.java
diff --git a/media/java/android/media/PublicFormatUtils.java b/core/java/android/media/PublicFormatUtils.java
similarity index 100%
rename from media/java/android/media/PublicFormatUtils.java
rename to core/java/android/media/PublicFormatUtils.java
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 58b2a67..a4fc342 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -259,19 +259,20 @@
         int FRAME_DEADLINE = 9;
         int FRAME_START_TIME = 10;
         int FRAME_INTERVAL = 11;
-        int SYNC_QUEUED = 12;
-        int SYNC_START = 13;
-        int ISSUE_DRAW_COMMANDS_START = 14;
-        int SWAP_BUFFERS = 15;
-        int FRAME_COMPLETED = 16;
-        int DEQUEUE_BUFFER_DURATION = 17;
-        int QUEUE_BUFFER_DURATION = 18;
-        int GPU_COMPLETED = 19;
-        int SWAP_BUFFERS_COMPLETED = 20;
-        int DISPLAY_PRESENT_TIME = 21;
-        int COMMAND_SUBMISSION_COMPLETED = 22;
+        int WORKLOAD_TARGET = 12;
+        int SYNC_QUEUED = 13;
+        int SYNC_START = 14;
+        int ISSUE_DRAW_COMMANDS_START = 15;
+        int SWAP_BUFFERS = 16;
+        int FRAME_COMPLETED = 17;
+        int DEQUEUE_BUFFER_DURATION = 18;
+        int QUEUE_BUFFER_DURATION = 19;
+        int GPU_COMPLETED = 20;
+        int SWAP_BUFFERS_COMPLETED = 21;
+        int DISPLAY_PRESENT_TIME = 22;
+        int COMMAND_SUBMISSION_COMPLETED = 23;
 
-        int FRAME_STATS_COUNT = 23; // must always be last and in sync with
+        int FRAME_STATS_COUNT = 24; // must always be last and in sync with
                                     // FrameInfoIndex::NumIndexes in libs/hwui/FrameInfo.h
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 8a10979..578b7b6 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -541,6 +541,22 @@
             "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
 
     /**
+     * Argument for specifying the extended selection.
+     *
+     * <p><strong>Type:</strong> {@link AccessibilityNodeInfo.Selection}<br>
+     * <strong>Actions:</strong>
+     *
+     * <ul>
+     *   <li>{@link AccessibilityAction#ACTION_SET_EXTENDED_SELECTION}
+     * </ul>
+     *
+     * @see AccessibilityAction#ACTION_SET_EXTENDED_SELECTION
+     */
+    @FlaggedApi(Flags.FLAG_A11Y_SELECTION_API)
+    public static final String ACTION_ARGUMENT_SELECTION_PARCELABLE =
+            "android.view.accessibility.action.ARGUMENT_SELECTION_PARCELABLE";
+
+    /**
      * Argument for whether when moving at granularity to extend the selection
      * or to move it otherwise.
      * <p>
@@ -1146,6 +1162,8 @@
 
     private int mConnectionId = UNDEFINED_CONNECTION_ID;
 
+    private Selection mSelection;
+
     private RangeInfo mRangeInfo;
     private CollectionInfo mCollectionInfo;
     private CollectionItemInfo mCollectionItemInfo;
@@ -2660,6 +2678,56 @@
     }
 
     /**
+     * Sets the extended selection, which is a representation of selection that spans multiple nodes
+     * that exist within the subtree of the node defining selection.
+     *
+     * <p><b>Note:</b> The start and end {@link SelectionPosition} of the provided {@link Selection}
+     * should be constructed with {@code this} node or a descendant of it.
+     *
+     * <p><b>Note:</b> {@link AccessibilityNodeInfo#setFocusable} and {@link
+     * AccessibilityNodeInfo#setFocused} should both be called with {@code true} before setting the
+     * selection in order to make {@code this} node a candidate to contain a selection.
+     *
+     * <p><b>Note:</b> Cannot be called from an AccessibilityService. This class is made immutable
+     * before being delivered to an AccessibilityService.
+     *
+     * @param selection The extended selection within the node's subtree, or {@code null} if no
+     *     selection exists.
+     * @see AccessibilityNodeInfo.AccessibilityAction#ACTION_SET_EXTENDED_SELECTION
+     * @throws IllegalStateException If called from an AccessibilityService
+     */
+    @FlaggedApi(Flags.FLAG_A11Y_SELECTION_API)
+    public void setSelection(@Nullable Selection selection) {
+        enforceNotSealed();
+        mSelection = selection;
+    }
+
+    /**
+     * Gets the extended selection, which is a representation of selection that spans multiple nodes
+     * that exist within the subtree of the node defining selection.
+     *
+     * <p><b>Note:</b> The start and end {@link SelectionPosition} of the provided {@link Selection}
+     * should be constructed with {@code this} node or a descendant of it.
+     *
+     * <p><b>Note:</b> In order for a node to be a candidate to contain a selection, {@link
+     * AccessibilityNodeInfo#isFocusable()} ()} and {@link AccessibilityNodeInfo#isFocused()} should
+     * both be return with {@code true}.
+     *
+     * @return The extended selection within the node's subtree, or {@code null} if no selection
+     *     exists.
+     */
+    @FlaggedApi(Flags.FLAG_A11Y_SELECTION_API)
+    public @Nullable Selection getSelection() {
+        if (mSelection != null) {
+            mSelection.getStart().setWindowId(mWindowId);
+            mSelection.getStart().setConnectionId(mConnectionId);
+            mSelection.getEnd().setWindowId(mWindowId);
+            mSelection.getEnd().setConnectionId(mConnectionId);
+        }
+        return mSelection;
+    }
+
+    /**
      * Gets whether this node is visible to the user.
      * <p>
      * Between {@link Build.VERSION_CODES#JELLY_BEAN API 16} and
@@ -4168,6 +4236,15 @@
      *         there is no text selection and no cursor.
      */
     public int getTextSelectionStart() {
+        if (Flags.a11ySelectionApi()) {
+            Selection current = getSelection();
+            if ((current != null)
+                    && current.getStart().usesNode(this)
+                    && current.getEnd().usesNode(this)) {
+                return current.getStart().getOffset();
+            }
+            return UNDEFINED_SELECTION_INDEX;
+        }
         return mTextSelectionStart;
     }
 
@@ -4183,6 +4260,15 @@
      *         there is no text selection and no cursor.
      */
     public int getTextSelectionEnd() {
+        if (Flags.a11ySelectionApi()) {
+            Selection current = getSelection();
+            if ((current != null)
+                    && current.getStart().usesNode(this)
+                    && current.getEnd().usesNode(this)) {
+                return current.getEnd().getOffset();
+            }
+            return UNDEFINED_SELECTION_INDEX;
+        }
         return mTextSelectionEnd;
     }
 
@@ -4201,6 +4287,13 @@
      */
     public void setTextSelection(int start, int end) {
         enforceNotSealed();
+        if (Flags.a11ySelectionApi()) {
+            Selection selection =
+                    new Selection(
+                            new SelectionPosition(this, start), new SelectionPosition(this, end));
+            setSelection(selection);
+            return;
+        }
         mTextSelectionStart = start;
         mTextSelectionEnd = end;
     }
@@ -4875,6 +4968,10 @@
             nonDefaultFields |= bitAt(fieldIndex);
         }
         fieldIndex++;
+        if (!Objects.equals(mSelection, DEFAULT.mSelection)) {
+            nonDefaultFields |= bitAt(fieldIndex);
+        }
+        fieldIndex++;
         if (mChecked != DEFAULT.mChecked) {
             nonDefaultFields |= bitAt(fieldIndex);
         }
@@ -5055,6 +5152,9 @@
             parcel.writeLong(mLeashedParentNodeId);
         }
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            mSelection.writeToParcel(parcel, flags);
+        }
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
             parcel.writeInt(mChecked);
         }
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
@@ -5172,6 +5272,17 @@
         ExtraRenderingInfo ti = other.mExtraRenderingInfo;
         mExtraRenderingInfo = (ti == null) ? null
                 : new ExtraRenderingInfo(ti);
+
+        if (Flags.a11ySelectionApi()) {
+            if (other.getSelection() != null) {
+                SelectionPosition sps = other.getSelection().getStart();
+                SelectionPosition spe = other.getSelection().getEnd();
+                mSelection =
+                        new Selection(
+                                new SelectionPosition(sps.mSourceNodeId, sps.getOffset()),
+                                new SelectionPosition(spe.mSourceNodeId, spe.getOffset()));
+            }
+        }
     }
 
     /**
@@ -5344,6 +5455,9 @@
             mLeashedParentNodeId = parcel.readLong();
         }
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            mSelection = Selection.CREATOR.createFromParcel(parcel);
+        }
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
             mChecked = parcel.readInt();
         }
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
@@ -5495,6 +5609,9 @@
                 if (action == R.id.accessibilityActionScrollInDirection) {
                     return "ACTION_SCROLL_IN_DIRECTION";
                 }
+                if (action == R.id.accessibilityActionSetExtendedSelection) {
+                    return "ACTION_SET_EXTENDED_SELECTION";
+                }
                 return "ACTION_UNKNOWN";
             }
         }
@@ -5696,6 +5813,271 @@
     }
 
     /**
+     * A class which defines either the start or end of a selection that can span across multiple
+     * AccessibilityNodeInfo objects.
+     *
+     * @see AccessibilityNodeInfo.Selection
+     */
+    @FlaggedApi(Flags.FLAG_A11Y_SELECTION_API)
+    public static final class SelectionPosition implements Parcelable {
+
+        private final int mOffset;
+        private final long mSourceNodeId;
+        private int mConnectionId;
+        private int mWindowId;
+
+        /**
+         * Instantiates a new SelectionPosition.
+         *
+         * @param node The {@link AccessibilityNodeInfo} for the node of this selection.
+         * @param offset The offset for a {@link SelectionPosition} within {@code view}'s text
+         *     content, which should be a value between 0 and the length of {@code view}'s text.
+         */
+        public SelectionPosition(@NonNull AccessibilityNodeInfo node, int offset) {
+            this(node.mSourceNodeId, offset);
+        }
+
+        /**
+         * Instantiates a new SelectionPosition.
+         *
+         * @param view The {@link View} containing the virtual descendant associated with the
+         *     selection position.
+         * @param offset The offset for a selection position within {@code view}'s text content,
+         *     which should be a value between 0 and the length of {@code view}'s text.
+         */
+        public SelectionPosition(@NonNull View view, int offset) {
+            this(
+                    makeNodeId(
+                            view.getAccessibilityViewId(), AccessibilityNodeProvider.HOST_VIEW_ID),
+                    offset);
+        }
+
+        /**
+         * Instantiates a new {@link SelectionPosition}.
+         *
+         * @param view The view whose virtual descendant is associated with the selection position.
+         * @param virtualDescendantId The ID of the virtual descendant within {@code view}'s virtual
+         *     subtree that contains the selection position.
+         * @param offset The offset for a selection position within the virtual descendant's text
+         *     content, which should be a value between 0 and the length of the descendant's text.
+         * @see AccessibilityNodeProvider
+         */
+        public SelectionPosition(@NonNull View view, int virtualDescendantId, int offset) {
+            this(makeNodeId(view.getAccessibilityViewId(), virtualDescendantId), offset);
+        }
+
+        private SelectionPosition(long sourceNodeId, int offset) {
+            mOffset = offset;
+            mSourceNodeId = sourceNodeId;
+        }
+
+        private SelectionPosition(Parcel in) {
+            mOffset = in.readInt();
+            mSourceNodeId = in.readLong();
+        }
+
+        private void setWindowId(int windowId) {
+            mWindowId = windowId;
+        }
+
+        private void setConnectionId(int connectionId) {
+            mConnectionId = connectionId;
+        }
+
+        /**
+         * Gets the node for {@code this} {@link SelectionPosition}
+         * <br>
+         * <strong>Note:</strong> This api can only be called from {@link AccessibilityService}.
+         *
+         * @return The node associated with {@code this} {@link SelectionPosition}
+         */
+        public @Nullable AccessibilityNodeInfo getNode() {
+            return getNodeForAccessibilityId(mConnectionId, mWindowId, mSourceNodeId);
+        }
+
+        /**
+         * Gets the offset for {@code this} {@link SelectionPosition}.
+         *
+         * @return A value from 0 to the length of {@link #getNode()}'s content representing the
+         *     offset of the {@link SelectionPosition}
+         */
+        public int getOffset() {
+            return mOffset;
+        }
+
+        private boolean usesNode(@NonNull AccessibilityNodeInfo node) {
+            return this.mSourceNodeId == node.mSourceNodeId
+                    && this.mConnectionId == node.mConnectionId
+                    && this.mWindowId == node.mWindowId;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other == null) {
+                return false;
+            }
+
+            if (other == this) {
+                return true;
+            }
+
+            if (getClass() != other.getClass()) {
+                return false;
+            }
+
+            SelectionPosition rhs = (SelectionPosition) other;
+            if (getOffset() != rhs.getOffset()) {
+                return false;
+            }
+
+            return mSourceNodeId == rhs.mSourceNodeId;
+        }
+
+        @Override
+        public int hashCode() {
+            final long prime = 877;
+            long result = 1;
+
+            if (mOffset != 0) {
+                result *= mOffset;
+            }
+
+            if (mSourceNodeId != UNDEFINED_NODE_ID) {
+                result *= mSourceNodeId;
+            }
+
+            return Long.hashCode(result * prime);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mOffset);
+            dest.writeLong(mSourceNodeId);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        /**
+         * @see android.os.Parcelable.Creator
+         */
+        @NonNull
+        public static final Creator<SelectionPosition> CREATOR =
+                new Creator<SelectionPosition>() {
+                    @Override
+                    public SelectionPosition createFromParcel(Parcel in) {
+                        return new SelectionPosition(in);
+                    }
+
+                    @Override
+                    public SelectionPosition[] newArray(int size) {
+                        return new SelectionPosition[size];
+                    }
+                };
+    }
+
+    /**
+     * Represents a selection of content that may extend across more than one {@link
+     * AccessibilityNodeInfo} instance.
+     *
+     * @see AccessibilityNodeInfo.SelectionPosition
+     */
+    @FlaggedApi(Flags.FLAG_A11Y_SELECTION_API)
+    public static final class Selection implements Parcelable {
+
+        private final SelectionPosition mStart;
+        private final SelectionPosition mEnd;
+
+        /**
+         * Instantiates a new Selection.
+         *
+         * @param start The start of the extended selection.
+         * @param end The end of the extended selection.
+         */
+        public Selection(@NonNull SelectionPosition start, @NonNull SelectionPosition end) {
+            this.mStart = start;
+            this.mEnd = end;
+        }
+
+        private Selection(Parcel in) {
+            mStart = SelectionPosition.CREATOR.createFromParcel(in);
+            mEnd = SelectionPosition.CREATOR.createFromParcel(in);
+        }
+
+        /**
+         * @return The start of the extended selection.
+         */
+        public @NonNull SelectionPosition getStart() {
+            return mStart;
+        }
+
+        /**
+         * @return The end of the extended selection.
+         */
+        public @NonNull SelectionPosition getEnd() {
+            return mEnd;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null) {
+                return false;
+            }
+
+            if (obj == this) {
+                return true;
+            }
+
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+
+            Selection rhs = (Selection) obj;
+            return getStart().equals(rhs.getStart()) && getEnd().equals(rhs.getEnd());
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 17;
+            return prime * getStart().hashCode() * getEnd().hashCode();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            mStart.writeToParcel(dest, flags);
+            mEnd.writeToParcel(dest, flags);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        /**
+         * @see android.os.Parcelable.Creator
+         */
+        @NonNull
+        public static final Creator<Selection> CREATOR =
+                new Creator<Selection>() {
+                    @Override
+                    public Selection createFromParcel(Parcel in) {
+                        return new Selection(in);
+                    }
+
+                    @Override
+                    public Selection[] newArray(int size) {
+                        return new Selection[size];
+                    }
+                };
+    }
+
+    /**
      * A class defining an action that can be performed on an {@link AccessibilityNodeInfo}.
      * Each action has a unique id that is mandatory and optional data.
      * <p>
@@ -6419,6 +6801,29 @@
         @NonNull public static final AccessibilityAction ACTION_SHOW_TEXT_SUGGESTIONS =
                 new AccessibilityAction(R.id.accessibilityActionShowTextSuggestions);
 
+        /**
+         * Action to set the extended selection. Performing this action with no arguments clears the
+         * selection.
+         *
+         * <p><strong>Arguments:</strong> {@link
+         * AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_PARCELABLE
+         * AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_PARCELABLE}<br>
+         * <strong>Example:</strong> <code><pre><p>
+         *  Bundle arguments = new Bundle();
+         *  Selection selection = new Selection(null, null);
+         *  arguments.setParcelable(
+         *          AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_PARCELABLE, selection);
+         *  info.performAction(
+         *          AccessibilityAction.ACTION_SET_EXTENDED_SELECTION.getId(), arguments);
+         * </pre></code>
+         *
+         * @see AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_PARCELABLE
+         */
+        @FlaggedApi(Flags.FLAG_A11Y_SELECTION_API)
+        @NonNull
+        public static final AccessibilityAction ACTION_SET_EXTENDED_SELECTION =
+                new AccessibilityAction(R.id.accessibilityActionSetExtendedSelection);
+
         private final int mActionId;
         private final CharSequence mLabel;
 
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 9a5849a..641ecc9 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -399,7 +399,9 @@
     @RemotableViewMethod(asyncImpl = "setIsCollapsedAsync")
     public void setIsCollapsed(boolean isCollapsed) {
         mIsCollapsed = isCollapsed;
-        mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed ? 1 : Integer.MAX_VALUE);
+        mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed
+                ? TextUtils.isEmpty(mSummarizedContent) ? 1 : 2
+                : Integer.MAX_VALUE);
         updateExpandButton();
         updateContentEndPaddings();
     }
@@ -448,7 +450,7 @@
 
         List<MessagingMessage> newMessagingMessages;
         mSummarizedContent = extras.getCharSequence(Notification.EXTRA_SUMMARIZED_CONTENT);
-        if (mSummarizedContent != null && mIsCollapsed) {
+        if (!TextUtils.isEmpty(mSummarizedContent) && mIsCollapsed) {
             Notification.MessagingStyle.Message summary =
                     new Notification.MessagingStyle.Message(mSummarizedContent,  0, "");
             newMessagingMessages = createMessages(List.of(summary), false, usePrecomputedText);
@@ -1162,7 +1164,7 @@
                 nameOverride = mNameReplacement;
             }
             newGroup.setShowingAvatar(!mIsOneToOne && !mIsCollapsed);
-            newGroup.setSingleLine(mIsCollapsed);
+            newGroup.setSingleLine(mIsCollapsed && TextUtils.isEmpty(mSummarizedContent));
             newGroup.setSender(sender, nameOverride);
             newGroup.setSending(groupIndex == (groups.size() - 1) && showSpinner);
             mGroups.add(newGroup);
@@ -1462,7 +1464,6 @@
                 maxHeight = Math.max(maxHeight,
                         child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
             }
-
             maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
             if (maxHeight != getMeasuredHeight()) {
                 setMeasuredDimension(getMeasuredWidth(), maxHeight);
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 90ab660..e9d920c 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -198,7 +198,8 @@
                 /* isHistoric= */true, usePrecomputedText);
         List<MessagingMessage> newMessagingMessages;
         mSummarizedContent = extras.getCharSequence(Notification.EXTRA_SUMMARIZED_CONTENT);
-        if (mSummarizedContent != null && mIsCollapsed) {
+        if (!TextUtils.isEmpty(mSummarizedContent) && mIsCollapsed) {
+            mMessagingLinearLayout.setMaxDisplayedLines(2);
             Notification.MessagingStyle.Message summary =
                     new Notification.MessagingStyle.Message(mSummarizedContent,  0, "");
             newMessagingMessages = createMessages(List.of(summary), false, usePrecomputedText);
@@ -488,7 +489,7 @@
             if (sender != mUser && mNameReplacement != null) {
                 nameOverride = mNameReplacement;
             }
-            newGroup.setSingleLine(mIsCollapsed);
+            newGroup.setSingleLine(mIsCollapsed && TextUtils.isEmpty(mSummarizedContent));
             newGroup.setShowingAvatar(!mIsCollapsed);
             newGroup.setSender(sender, nameOverride);
             newGroup.setSending(groupIndex == (groups.size() - 1) && showSpinner);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 447822f..06702e2 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -134,6 +134,10 @@
                 "android_app_ActivityThread.cpp",
                 "android_app_NativeActivity.cpp",
                 "android_app_admin_SecurityLog.cpp",
+                "android_media_ImageReader.cpp",
+                "android_media_ImageWriter.cpp",
+                "android_media_PublicFormatUtils.cpp",
+                "android_media_Utils.cpp",
                 "android_opengl_EGL14.cpp",
                 "android_opengl_EGL15.cpp",
                 "android_opengl_EGLExt.cpp",
@@ -531,3 +535,35 @@
         "vintf",
     ],
 }
+
+cc_library_shared {
+    name: "libmedia_jni_utils",
+    srcs: [
+        ":libgui_frame_event_aidl",
+        "android_media_Utils.cpp",
+    ],
+
+    header_libs: [
+        "libgui_headers",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libui",
+        "libutils",
+    ],
+
+    include_dirs: [
+        "system/media/camera/include",
+    ],
+
+    export_include_dirs: ["."],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-error=deprecated-declarations",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 5c0b720..b2b8263 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -102,7 +102,10 @@
 extern int register_android_media_AudioProductStrategies(JNIEnv *env);
 extern int register_android_media_AudioVolumeGroups(JNIEnv *env);
 extern int register_android_media_AudioVolumeGroupChangeHandler(JNIEnv *env);
+extern int register_android_media_ImageReader(JNIEnv *env);
+extern int register_android_media_ImageWriter(JNIEnv *env);
 extern int register_android_media_MicrophoneInfo(JNIEnv *env);
+extern int register_android_media_PublicFormatUtils(JNIEnv *env);
 extern int register_android_media_ToneGenerator(JNIEnv *env);
 extern int register_android_media_audio_common_AidlConversion(JNIEnv* env);
 extern int register_android_media_midi(JNIEnv *env);
@@ -1658,8 +1661,11 @@
         REG_JNI(register_android_media_AudioProductStrategies),
         REG_JNI(register_android_media_AudioVolumeGroups),
         REG_JNI(register_android_media_AudioVolumeGroupChangeHandler),
+        REG_JNI(register_android_media_ImageReader),
+        REG_JNI(register_android_media_ImageWriter),
         REG_JNI(register_android_media_MediaMetrics),
         REG_JNI(register_android_media_MicrophoneInfo),
+        REG_JNI(register_android_media_PublicFormatUtils),
         REG_JNI(register_android_media_RemoteDisplay),
         REG_JNI(register_android_media_ToneGenerator),
         REG_JNI(register_android_media_audio_common_AidlConversion),
diff --git a/media/jni/android_media_ImageReader.cpp b/core/jni/android_media_ImageReader.cpp
similarity index 99%
rename from media/jni/android_media_ImageReader.cpp
rename to core/jni/android_media_ImageReader.cpp
index effa92c..20b9c57 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/core/jni/android_media_ImageReader.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#undef ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION // TODO:remove this and fix code
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "ImageReader_JNI"
diff --git a/media/jni/android_media_ImageWriter.cpp b/core/jni/android_media_ImageWriter.cpp
similarity index 99%
rename from media/jni/android_media_ImageWriter.cpp
rename to core/jni/android_media_ImageWriter.cpp
index 93deb51..1357dd8 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/core/jni/android_media_ImageWriter.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#undef ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION // TODO:remove this and fix code
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "ImageWriter_JNI"
diff --git a/media/jni/android_media_PublicFormatUtils.cpp b/core/jni/android_media_PublicFormatUtils.cpp
similarity index 100%
rename from media/jni/android_media_PublicFormatUtils.cpp
rename to core/jni/android_media_PublicFormatUtils.cpp
diff --git a/media/jni/android_media_Utils.cpp b/core/jni/android_media_Utils.cpp
similarity index 100%
rename from media/jni/android_media_Utils.cpp
rename to core/jni/android_media_Utils.cpp
diff --git a/media/jni/android_media_Utils.h b/core/jni/android_media_Utils.h
similarity index 100%
rename from media/jni/android_media_Utils.h
rename to core/jni/android_media_Utils.h
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 595160e..e9d87e4 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -431,15 +431,18 @@
     <!-- The minimum height of the notification content (even when there's only one line of text) -->
     <dimen name="notification_2025_content_min_height">40dp</dimen>
 
-    <!-- Height of a headerless notification with one or two lines -->
-    <!-- 16 * 2 (margins) + 40 (min content height) = 72 (notification) -->
+    <!-- Max height of a collapsed (headerless) notification with a summarization -->
+    <dimen name="notification_collapsed_height_with_summarization">156dp</dimen>
+
+    <!-- Max height of a collapsed (headerless) notification with one or two lines -->
+    <!-- 16 * 2 (margins) + 48 (min content height) = 72 (notification) -->
     <dimen name="notification_2025_min_height">72dp</dimen>
 
     <!-- Height of a headerless notification with one line -->
     <!-- 16 * 2 (margins) + 24 (1 line) = 56 (notification) -->
     <dimen name="notification_headerless_min_height">56dp</dimen>
 
-    <!-- Height of a small two-line notification -->
+    <!-- Max height of a collapsed two-line notification -->
     <!-- 20 * 2 (margins) + 24 * 2 (2 lines) = 88 (notification) -->
     <dimen name="notification_min_height">88dp</dimen>
 
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 3b39a65..5b5ef09 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -157,6 +157,9 @@
   <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_CONTEXT_CLICK}. -->
   <item type="id" name="accessibilityActionContextClick" />
 
+  <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SET_EXTENDED_SELECTION}. -->
+  <item type="id" name="accessibilityActionSetExtendedSelection" />
+
   <item type="id" name="remote_input_tag" />
   <item type="id" name="pending_intent_tag" />
   <item type="id" name="remote_checked_change_listener_tag" />
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 2d411d0..e3137e2 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -129,6 +129,8 @@
   <staging-public-group type="id" first-id="0x01b20000">
     <!-- @FlaggedApi(android.appwidget.flags.Flags.FLAG_ENGAGEMENT_METRICS) -->
     <public name="remoteViewsMetricsId"/>
+    <!-- @FlaggedApi("android.view.accessibility.a11y_selection_api")  -->
+    <public name="accessibilityActionSetExtendedSelection"/>
   </staging-public-group>
 
   <staging-public-group type="style" first-id="0x01b10000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cc2897a..da6fb3b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5899,6 +5899,7 @@
 
   <java-symbol type="array" name="config_notificationDefaultUnsupportedAdjustments" />
   <java-symbol type="drawable" name="ic_notification_summarization" />
+  <java-symbol type="dimen" name="notification_collapsed_height_with_summarization" />
 
   <!-- Advanced Protection Service USB feature -->
   <java-symbol type="string" name="usb_apm_usb_plugged_in_when_locked_notification_title" />
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 3b0eab4..cc5c6af 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@
     // The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
     // See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
     // and assertAccessibilityNodeInfoCleared in that class.
-    private static final int NUM_MARSHALLED_PROPERTIES = 47;
+    private static final int NUM_MARSHALLED_PROPERTIES = 48;
 
     /**
      * The number of properties that are purposely not marshalled
diff --git a/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java b/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java
index 52ff79d..2edab62 100644
--- a/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java
@@ -22,6 +22,8 @@
 
 import android.os.SystemClock;
 
+import android.os.SystemClock;
+
 import androidx.test.runner.AndroidJUnit4;
 
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -54,13 +56,13 @@
      */
     @Test
     public void testTtl_Zero() {
-        TestRateLimitingCache<Integer> s = new TestRateLimitingCache<>(0);
+        RateLimitingCache<Integer> s = new RateLimitingCache<>(0);
 
         int first = s.get(mFetcher);
         assertEquals(first, 0);
         int second = s.get(mFetcher);
         assertEquals(second, 1);
-        s.advanceTime(20);
+        SystemClock.sleep(20);
         int third = s.get(mFetcher);
         assertEquals(third, 2);
     }
@@ -71,14 +73,14 @@
      */
     @Test
     public void testTtl_100() {
-        TestRateLimitingCache<Integer> s = new TestRateLimitingCache<>(100);
+        RateLimitingCache<Integer> s = new RateLimitingCache<>(100);
 
         int first = s.get(mFetcher);
         assertEquals(first, 0);
         int second = s.get(mFetcher);
         // Too early to change
         assertEquals(second, 0);
-        s.advanceTime(150);
+        SystemClock.sleep(150);
         int third = s.get(mFetcher);
         // Changed by now
         assertEquals(third, 1);
@@ -93,11 +95,11 @@
      */
     @Test
     public void testTtl_Negative() {
-        TestRateLimitingCache<Integer> s = new TestRateLimitingCache<>(-1);
+        RateLimitingCache<Integer> s = new RateLimitingCache<>(-1);
 
         int first = s.get(mFetcher);
         assertEquals(first, 0);
-        s.advanceTime(200);
+        SystemClock.sleep(200);
         // Should return the original value every time
         int second = s.get(mFetcher);
         assertEquals(second, 0);
@@ -109,7 +111,7 @@
      */
     @Test
     public void testTtl_Spam() {
-        TestRateLimitingCache<Integer> s = new TestRateLimitingCache<>(100);
+        RateLimitingCache<Integer> s = new RateLimitingCache<>(100);
         assertCount(s, 1000, 7, 15);
     }
 
@@ -119,13 +121,28 @@
      */
     @Test
     public void testRate_10hz() {
-        TestRateLimitingCache<Integer> s = new TestRateLimitingCache<>(1000, 10);
+        RateLimitingCache<Integer> s = new RateLimitingCache<>(1000, 10);
         // At 10 per second, 2 seconds should not exceed about 30, assuming overlap into left and
         // right windows that allow 10 each
         assertCount(s, 2000, 20, 33);
     }
 
     /**
+     * Test that using a different timebase works correctly.
+     */
+    @Test
+    public void testTimebase() {
+        RateLimitingCache<Integer> s = new RateLimitingCache<>(1000, 10) {
+            @Override
+            protected long getTime() {
+                return SystemClock.elapsedRealtime() / 2;
+            }
+        };
+        // Timebase is moving at half the speed, so only allows for 1 second worth in 2 seconds.
+        assertCount(s, 2000, 10, 22);
+    }
+
+    /**
      * Exercises concurrent access to the cache.
      */
     @Test
@@ -270,36 +287,15 @@
      * @param minCount the lower end of the expected number of fetches, with a margin for error
      * @param maxCount the higher end of the expected number of fetches, with a margin for error
      */
-    private void assertCount(TestRateLimitingCache<Integer> cache, long period,
+    private void assertCount(RateLimitingCache<Integer> cache, long period,
             int minCount, int maxCount) {
-        long startTime = cache.getTime();
-        while (cache.getTime() < startTime + period) {
+        long startTime = SystemClock.elapsedRealtime();
+        while (SystemClock.elapsedRealtime() < startTime + period) {
             int value = cache.get(mFetcher);
-            cache.advanceTime(5);
+            SystemClock.sleep(5);
         }
         int latest = cache.get(mFetcher);
         assertTrue("Latest should be between " + minCount + " and " + maxCount
                         + " but is " + latest, latest <= maxCount && latest >= minCount);
     }
-
-    private static class TestRateLimitingCache<Value> extends RateLimitingCache<Value> {
-        private long mTime;
-
-        public TestRateLimitingCache(long periodMillis) {
-            super(periodMillis);
-        }
-
-        public TestRateLimitingCache(long periodMillis, int count) {
-            super(periodMillis, count);
-        }
-
-        public void advanceTime(long time) {
-            mTime += time;
-        }
-
-        @Override
-        public long getTime() {
-            return mTime;
-        }
-    }
 }
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java
index 8f12828..7d236d2 100644
--- a/graphics/java/android/graphics/FrameInfo.java
+++ b/graphics/java/android/graphics/FrameInfo.java
@@ -95,7 +95,8 @@
 
     // Must be the last one
     // This value must be in sync with `UI_THREAD_FRAME_INFO_SIZE` in FrameInfo.h
-    private static final int FRAME_INFO_SIZE = FRAME_INTERVAL + 1;
+    // In calculating size, + 1 for Flags, and + 1 for WorkloadTarget from FrameInfo.h
+    private static final int FRAME_INFO_SIZE = FRAME_INTERVAL + 2;
 
     /** checkstyle */
     public void setVsync(long intendedVsync, long usedVsync, long frameTimelineVsyncId,
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index 50c0873..477d207 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -41,7 +41,7 @@
             android:id="@+id/application_icon"
             android:layout_width="@dimen/desktop_mode_caption_icon_radius"
             android:layout_height="@dimen/desktop_mode_caption_icon_radius"
-            android:layout_marginStart="12dp"
+            android:layout_marginStart="10dp"
             android:layout_marginEnd="12dp"
             android:contentDescription="@string/app_icon_text"
             android:importantForAccessibility="no"/>
@@ -53,10 +53,9 @@
 
         <com.android.wm.shell.windowdecor.HandleMenuImageButton
             android:id="@+id/collapse_menu_button"
-            android:layout_width="32dp"
-            android:layout_height="32dp"
-            android:padding="4dp"
-            android:layout_marginEnd="14dp"
+            android:layout_width="16dp"
+            android:layout_height="16dp"
+            android:layout_marginEnd="16dp"
             android:layout_marginStart="14dp"
             android:contentDescription="@string/collapse_menu_text"
             android:src="@drawable/ic_baseline_expand_more_24"
@@ -78,40 +77,55 @@
 
         <ImageButton
             android:id="@+id/fullscreen_button"
-            android:layout_marginEnd="4dp"
+            android:paddingStart="16dp"
+            android:paddingEnd="12dp"
             android:contentDescription="@string/fullscreen_text"
             android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen"
             android:tint="@androidprv:color/materialColorOnSurface"
-            android:layout_weight="1"
             style="@style/DesktopModeHandleMenuWindowingButton"/>
 
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_weight="1"/>
+
         <ImageButton
             android:id="@+id/split_screen_button"
-            android:layout_marginStart="4dp"
-            android:layout_marginEnd="4dp"
+            android:paddingStart="14dp"
+            android:paddingEnd="14dp"
             android:contentDescription="@string/split_screen_text"
             android:src="@drawable/desktop_mode_ic_handle_menu_splitscreen"
             android:tint="@androidprv:color/materialColorOnSurface"
-            android:layout_weight="1"
             style="@style/DesktopModeHandleMenuWindowingButton"/>
 
+        <Space
+            android:id="@+id/floating_button_space"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_weight="1"/>
+
         <ImageButton
             android:id="@+id/floating_button"
-            android:layout_marginStart="4dp"
-            android:layout_marginEnd="4dp"
+            android:paddingStart="14dp"
+            android:paddingEnd="14dp"
             android:contentDescription="@string/float_button_text"
             android:src="@drawable/desktop_mode_ic_handle_menu_floating"
             android:tint="@androidprv:color/materialColorOnSurface"
-            android:layout_weight="1"
             style="@style/DesktopModeHandleMenuWindowingButton"/>
 
+        <Space
+            android:id="@+id/desktop_button_space"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_weight="1"/>
+
         <ImageButton
             android:id="@+id/desktop_button"
-            android:layout_marginStart="4dp"
+            android:paddingStart="12dp"
+            android:paddingEnd="16dp"
             android:contentDescription="@string/desktop_text"
             android:src="@drawable/desktop_mode_ic_handle_menu_desktop"
             android:tint="@androidprv:color/materialColorOnSurface"
-            android:layout_weight="1"
             style="@style/DesktopModeHandleMenuWindowingButton"/>
 
     </LinearLayout>
@@ -126,77 +140,33 @@
         android:elevation="@dimen/desktop_mode_handle_menu_pill_elevation"
         android:background="@drawable/desktop_mode_decor_handle_menu_background">
 
-        <LinearLayout
+        <com.android.wm.shell.windowdecor.HandleMenuActionButton
             android:id="@+id/screenshot_button"
             android:contentDescription="@string/screenshot_text"
-            style="@style/DesktopModeHandleMenuActionButtonLayout">
+            android:text="@string/screenshot_text"
+            android:src="@drawable/desktop_mode_ic_handle_menu_screenshot"
+            style="@style/DesktopModeHandleMenuActionButton"/>
 
-            <ImageView
-                android:id="@+id/image"
-                android:src="@drawable/desktop_mode_ic_handle_menu_screenshot"
-                android:importantForAccessibility="no"
-                style="@style/DesktopModeHandleMenuActionButtonImage"/>
-
-            <com.android.wm.shell.windowdecor.MarqueedTextView
-                android:id="@+id/label"
-                android:text="@string/screenshot_text"
-                style="@style/DesktopModeHandleMenuActionButtonTextView"/>
-
-        </LinearLayout>
-
-        <LinearLayout
+        <com.android.wm.shell.windowdecor.HandleMenuActionButton
             android:id="@+id/new_window_button"
             android:contentDescription="@string/new_window_text"
-            style="@style/DesktopModeHandleMenuActionButtonLayout">
+            android:text="@string/new_window_text"
+            android:src="@drawable/desktop_mode_ic_handle_menu_new_window"
+            style="@style/DesktopModeHandleMenuActionButton"/>
 
-            <ImageView
-                android:id="@+id/image"
-                android:src="@drawable/desktop_mode_ic_handle_menu_new_window"
-                android:importantForAccessibility="no"
-                style="@style/DesktopModeHandleMenuActionButtonImage"/>
-
-            <com.android.wm.shell.windowdecor.MarqueedTextView
-                android:id="@+id/label"
-                android:text="@string/new_window_text"
-                style="@style/DesktopModeHandleMenuActionButtonTextView"/>
-
-        </LinearLayout>
-
-        <LinearLayout
+        <com.android.wm.shell.windowdecor.HandleMenuActionButton
             android:id="@+id/manage_windows_button"
             android:contentDescription="@string/manage_windows_text"
-            style="@style/DesktopModeHandleMenuActionButtonLayout">
+            android:text="@string/manage_windows_text"
+            android:src="@drawable/desktop_mode_ic_handle_menu_manage_windows"
+            style="@style/DesktopModeHandleMenuActionButton"/>
 
-            <ImageView
-                android:id="@+id/image"
-                android:src="@drawable/desktop_mode_ic_handle_menu_manage_windows"
-                android:importantForAccessibility="no"
-                style="@style/DesktopModeHandleMenuActionButtonImage"/>
-
-            <com.android.wm.shell.windowdecor.MarqueedTextView
-                android:id="@+id/label"
-                android:text="@string/manage_windows_text"
-                style="@style/DesktopModeHandleMenuActionButtonTextView"/>
-
-        </LinearLayout>
-
-        <LinearLayout
+        <com.android.wm.shell.windowdecor.HandleMenuActionButton
             android:id="@+id/change_aspect_ratio_button"
             android:contentDescription="@string/change_aspect_ratio_text"
-            style="@style/DesktopModeHandleMenuActionButtonLayout">
-
-            <ImageView
-                android:id="@+id/image"
-                android:src="@drawable/desktop_mode_ic_handle_menu_change_aspect_ratio"
-                android:importantForAccessibility="no"
-                style="@style/DesktopModeHandleMenuActionButtonImage"/>
-
-            <com.android.wm.shell.windowdecor.MarqueedTextView
-                android:id="@+id/label"
-                android:text="@string/change_aspect_ratio_text"
-                style="@style/DesktopModeHandleMenuActionButtonTextView"/>
-
-        </LinearLayout>
+            android:text="@string/change_aspect_ratio_text"
+            android:src="@drawable/desktop_mode_ic_handle_menu_change_aspect_ratio"
+            style="@style/DesktopModeHandleMenuActionButton"/>
     </LinearLayout>
 
     <LinearLayout
@@ -209,29 +179,14 @@
         android:elevation="@dimen/desktop_mode_handle_menu_pill_elevation"
         android:background="@drawable/desktop_mode_decor_handle_menu_background">
 
-        <LinearLayout
+        <com.android.wm.shell.windowdecor.HandleMenuActionButton
             android:id="@+id/open_in_app_or_browser_button"
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:layout_marginEnd="8dp"
-            android:gravity="start|center_vertical"
-            android:paddingStart="16dp"
             android:contentDescription="@string/open_in_browser_text"
-            android:background="?android:selectableItemBackground">
-
-            <ImageView
-                android:id="@+id/image"
-                android:src="@drawable/desktop_mode_ic_handle_menu_open_in_browser"
-                android:importantForAccessibility="no"
-                style="@style/DesktopModeHandleMenuActionButtonImage"/>
-
-            <com.android.wm.shell.windowdecor.MarqueedTextView
-                android:id="@+id/label"
-                android:text="@string/open_in_browser_text"
-                style="@style/DesktopModeHandleMenuActionButtonTextView"/>
-
-        </LinearLayout>
+            android:text="@string/open_in_browser_text"
+            android:src="@drawable/desktop_mode_ic_handle_menu_open_in_browser"
+            style="@style/DesktopModeHandleMenuActionButton"
+            android:layout_width="0dp"
+            android:layout_weight="1"/>
 
         <ImageButton
             android:id="@+id/open_by_default_button"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
new file mode 100644
index 0000000..379f4e9
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2025 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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/action_button"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="start|center_vertical"
+    android:paddingHorizontal="16dp"
+    android:clickable="true"
+    android:focusable="true"
+    android:orientation="horizontal"
+    android:background="?android:attr/selectableItemBackground">
+
+    <ImageView
+        android:id="@+id/image"
+        android:contentDescription="@+id/label"
+        style="@style/DesktopModeHandleMenuActionButtonImage"/>
+
+    <com.android.wm.shell.windowdecor.MarqueedTextView
+        android:id="@+id/label"
+        style="@style/DesktopModeHandleMenuActionButtonTextView"/>
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/values/attrs.xml b/libs/WindowManager/Shell/res/values/attrs.xml
index fbb5caa..4ba0468 100644
--- a/libs/WindowManager/Shell/res/values/attrs.xml
+++ b/libs/WindowManager/Shell/res/values/attrs.xml
@@ -23,4 +23,11 @@
     <declare-styleable name="MessageState">
         <attr name="state_task_focused" format="boolean"/>
     </declare-styleable>
+
+    <declare-styleable name="HandleMenuActionButton">
+        <attr name="android:text" format="string" />
+        <attr name="android:textColor" format="color" />
+        <attr name="android:src" format="reference" />
+        <attr name="android:drawableTint" format="color" />
+    </declare-styleable>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index e23d572..96e008e 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -570,7 +570,7 @@
     <dimen name="desktop_mode_handle_menu_corner_radius">26dp</dimen>
 
     <!-- The radius of the caption menu icon. -->
-    <dimen name="desktop_mode_caption_icon_radius">24dp</dimen>
+    <dimen name="desktop_mode_caption_icon_radius">32dp</dimen>
 
     <!-- The radius of the caption menu shadow. -->
     <dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 035004b..637b47a 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -40,13 +40,11 @@
         <item name="android:activityCloseExitAnimation">@anim/forced_resizable_exit</item>
     </style>
 
-    <style name="DesktopModeHandleMenuActionButtonLayout">
+    <style name="DesktopModeHandleMenuActionButton">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">52dp</item>
-        <item name="android:layout_weight">1</item>
-        <item name="android:gravity">start|center_vertical</item>
-        <item name="android:paddingHorizontal">16dp</item>
-        <item name="android:background">?android:selectableItemBackground</item>
+        <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
+        <item name="android:drawableTint">@androidprv:color/materialColorOnSurface</item>
     </style>
 
     <style name="DesktopModeHandleMenuActionButtonImage">
@@ -71,7 +69,6 @@
     <style name="DesktopModeHandleMenuWindowingButton">
         <item name="android:layout_width">48dp</item>
         <item name="android:layout_height">48dp</item>
-        <item name="android:padding">14dp</item>
         <item name="android:scaleType">fitCenter</item>
         <item name="android:background">?android:selectableItemBackgroundBorderless</item>
     </style>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index a17bcb3..ff50672 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -37,6 +37,9 @@
 import android.view.WindowManager
 import android.widget.ImageButton
 import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.Space
+import android.widget.TextView
 import android.window.DesktopModeFlags
 import android.window.SurfaceSyncGroup
 import androidx.annotation.StringRes
@@ -480,16 +483,23 @@
         private val splitscreenBtn = windowingPill.requireViewById<ImageButton>(
             R.id.split_screen_button)
         private val floatingBtn = windowingPill.requireViewById<ImageButton>(R.id.floating_button)
+        private val floatingBtnSpace = windowingPill.requireViewById<Space>(
+            R.id.floating_button_space)
+
         private val desktopBtn = windowingPill.requireViewById<ImageButton>(R.id.desktop_button)
+        private val desktopBtnSpace = windowingPill.requireViewById<Space>(
+            R.id.desktop_button_space)
 
         // More Actions Pill.
         private val moreActionsPill = rootView.requireViewById<View>(R.id.more_actions_pill)
-        private val screenshotBtn = moreActionsPill.requireViewById<View>(R.id.screenshot_button)
-        private val newWindowBtn = moreActionsPill.requireViewById<View>(R.id.new_window_button)
+        private val screenshotBtn = moreActionsPill.requireViewById<HandleMenuActionButton>(
+            R.id.screenshot_button)
+        private val newWindowBtn = moreActionsPill.requireViewById<HandleMenuActionButton>(
+            R.id.new_window_button)
         private val manageWindowBtn = moreActionsPill
-            .requireViewById<View>(R.id.manage_windows_button)
+            .requireViewById<HandleMenuActionButton>(R.id.manage_windows_button)
         private val changeAspectRatioBtn = moreActionsPill
-            .requireViewById<View>(R.id.change_aspect_ratio_button)
+            .requireViewById<HandleMenuActionButton>(R.id.change_aspect_ratio_button)
 
         // Open in Browser/App Pill.
         private val openInAppOrBrowserPill = rootView.requireViewById<View>(
@@ -682,6 +692,7 @@
 
             if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
                 floatingBtn.visibility = View.GONE
+                floatingBtnSpace.visibility = View.GONE
             }
 
             fullscreenBtn.isSelected = taskInfo.isFullscreen
@@ -710,8 +721,10 @@
             ).forEach {
                 val button = it.first
                 val shouldShow = it.second
-                val label = button.requireViewById<MarqueedTextView>(R.id.label)
-                val image = button.requireViewById<ImageView>(R.id.image)
+
+                val buttonRoot = button.requireViewById<LinearLayout>(R.id.action_button)
+                val label = buttonRoot.requireViewById<MarqueedTextView>(R.id.label)
+                val image = buttonRoot.requireViewById<ImageView>(R.id.image)
 
                 button.isGone = !shouldShow
                 label.apply {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
new file mode 100644
index 0000000..4b2e473
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2025 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.wm.shell.windowdecor
+
+import android.annotation.ColorInt
+import android.annotation.IdRes
+import android.content.Context
+import android.content.res.ColorStateList
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.core.content.withStyledAttributes
+import androidx.core.view.isGone
+import com.android.wm.shell.R
+
+/**
+ * Button-like component used to display the "Additional options" elements of the Handle menu window
+ *  decoration.
+ *
+ * The possible options for which this button is used for are "Screenshot", "New Window", "Manage
+ * Windows" and "Change Aspect Ratio".
+ */
+class HandleMenuActionButton @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr) {
+
+    private val rootElement: LinearLayout
+    private val iconView: ImageView
+    private val textView: MarqueedTextView
+
+    init {
+        val view = LayoutInflater.from(context).inflate(
+            R.layout.desktop_mode_window_decor_handle_menu_action_button, this, true)
+        rootElement = findViewById(R.id.action_button)
+        iconView = findViewById(R.id.image)
+        textView = findViewById(R.id.label)
+
+        context.withStyledAttributes(attrs, R.styleable.HandleMenuActionButton) {
+            textView.text = getString(R.styleable.HandleMenuActionButton_android_text)
+            textView.setTextColor(getColor(R.styleable.HandleMenuActionButton_android_textColor, 0))
+            iconView.setImageResource(getResourceId(
+                R.styleable.HandleMenuActionButton_android_src, 0))
+            iconView.imageTintList = getColorStateList(
+                R.styleable.HandleMenuActionButton_android_drawableTint)
+        }
+    }
+
+    /**
+     * Sets a listener to be invoked when this view is clicked.
+     *
+     * @param l the [OnClickListener] that receives click events.
+     */
+    override fun setOnClickListener(l: OnClickListener?) {
+        rootElement.setOnClickListener(l)
+    }
+
+    /**
+     * Sets the text color for the text inside the button.
+     *
+     * @param color the color to set for the text, as a color integer.
+     */
+    fun setTextColor(@ColorInt color: Int) {
+        textView.setTextColor(color)
+    }
+
+    /**
+     * Sets the icon for the button using a resource ID.
+     *
+     * @param resourceId the resource ID of the drawable to set as the icon.
+     */
+    fun setIconResource(@IdRes resourceId: Int) {
+        iconView.setImageResource(resourceId)
+    }
+
+    /**
+     * Sets the text to display inside the button.
+     *
+     * @param text the text to display.
+     */
+    fun setText(text: CharSequence?) {
+        textView.text = text
+    }
+
+    /**
+     * Sets the tint color for the icon.
+     *
+     * @param color the color to use for the tint, as a color integer.
+     */
+    fun setDrawableTint(@ColorInt color: Int) {
+        iconView.imageTintList = ColorStateList.valueOf(color)
+    }
+
+    /**
+     * Gets or sets the tint applied to the icon.
+     *
+     * @return The [ColorStateList] representing the tint, or null if no tint is applied.
+     */
+    var compoundDrawableTintList: ColorStateList?
+        get() = iconView.imageTintList
+        set(value) {
+            iconView.imageTintList = value
+        }
+}
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index a958a09..36feabd 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -32,8 +32,9 @@
                                 "PerformTraversalsStart",
                                 "DrawStart",
                                 "FrameDeadline",
-                                "FrameInterval",
                                 "FrameStartTime",
+                                "FrameInterval",
+                                "WorkloadTarget",
                                 "SyncQueued",
                                 "SyncStart",
                                 "IssueDrawCommandsStart",
@@ -48,7 +49,7 @@
 
 };
 
-static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 23,
+static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 24,
               "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)");
 
 void FrameInfo::importUiThreadInfo(int64_t* info) {
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index f7ad139..61c30b8 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -30,7 +30,8 @@
 namespace android {
 namespace uirenderer {
 
-static constexpr size_t UI_THREAD_FRAME_INFO_SIZE = 12;
+// This value must be in sync with `FRAME_INFO_SIZE` in FrameInfo.Java
+static constexpr size_t UI_THREAD_FRAME_INFO_SIZE = 13;
 
 enum class FrameInfoIndex {
     Flags = 0,
@@ -47,6 +48,11 @@
     FrameInterval,
     // End of UI frame info
 
+    // The target workload duration based on the original frame deadline and
+    // and intended vsync. Counted in UI_THREAD_FRAME_INFO_SIZE so its value
+    // can be set in setVsync().
+    WorkloadTarget,
+
     SyncQueued,
 
     SyncStart,
@@ -109,6 +115,7 @@
         set(FrameInfoIndex::FrameStartTime) = vsyncTime;
         set(FrameInfoIndex::FrameDeadline) = frameDeadline;
         set(FrameInfoIndex::FrameInterval) = frameInterval;
+        set(FrameInfoIndex::WorkloadTarget) = frameDeadline - intendedVsync;
         return *this;
     }
 
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 638a060..80eb6bc 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -201,7 +201,7 @@
     // If we are in triple buffering, we have enough buffers in queue to sustain a single frame
     // drop without jank, so adjust the frame interval to the deadline.
     if (isTripleBuffered) {
-        int64_t originalDeadlineDuration = deadline - frame[FrameInfoIndex::IntendedVsync];
+        int64_t originalDeadlineDuration = frame[FrameInfoIndex::WorkloadTarget];
         deadline = mNextFrameStartUnstuffed + originalDeadlineDuration;
         frame.set(FrameInfoIndex::FrameDeadline) = deadline;
     }
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 7d01dfb..21430f7 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -57,6 +57,9 @@
 constexpr bool early_preload_gl_context() {
     return false;
 }
+constexpr bool calc_workload_orig_deadline() {
+    return false;
+}
 }  // namespace hwui_flags
 #endif
 
@@ -299,5 +302,10 @@
                                  hwui_flags::early_preload_gl_context());
 }
 
+bool Properties::calcWorkloadOrigDeadline() {
+    static bool sCalcWorkloadOrigDeadline = base::GetBoolProperty(
+            "debug.hwui.calc_workload_orig_deadline", hwui_flags::calc_workload_orig_deadline());
+    return sCalcWorkloadOrigDeadline;
+}
 }  // namespace uirenderer
 }  // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 280a75a..a7a5644 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -384,6 +384,7 @@
     static bool initializeGlAlways();
     static bool resampleGainmapRegions();
     static bool earlyPreloadGlContext();
+    static bool calcWorkloadOrigDeadline();
 
 private:
     static StretchEffectBehavior stretchEffectBehavior;
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index 2851dd8..62fd7d3 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -176,4 +176,15 @@
   namespace: "core_graphics"
   description: "Initialize GL context and GraphicBufferAllocater init on renderThread preload. This improves app startup time for apps using GL."
   bug: "383612849"
+}
+
+flag {
+  name: "calc_workload_orig_deadline"
+  namespace: "window_surfaces"
+  description: "Use original frame deadline to calculate the workload target deadline for jank tracking"
+  bug: "389939827"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
\ No newline at end of file
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b36b8be..e3e393c 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -789,7 +789,13 @@
     int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline);
     int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
 
-    mHintSessionWrapper->updateTargetWorkDuration(frameDeadline - intendedVsync);
+    if (Properties::calcWorkloadOrigDeadline()) {
+        // Uses the unmodified frame deadline in calculating workload target duration
+        mHintSessionWrapper->updateTargetWorkDuration(
+                mCurrentFrameInfo->get(FrameInfoIndex::WorkloadTarget));
+    } else {
+        mHintSessionWrapper->updateTargetWorkDuration(frameDeadline - intendedVsync);
+    }
 
     if (didDraw) {
         int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
diff --git a/libs/hwui/tests/unit/JankTrackerTests.cpp b/libs/hwui/tests/unit/JankTrackerTests.cpp
index b67e419..c289d67 100644
--- a/libs/hwui/tests/unit/JankTrackerTests.cpp
+++ b/libs/hwui/tests/unit/JankTrackerTests.cpp
@@ -45,6 +45,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 115_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
+    info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
     jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     info = jankTracker.startFrame();
@@ -55,6 +56,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 131_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
+    info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
     jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(2, container.get()->totalFrameCount());
@@ -79,6 +81,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
+    info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
     jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(1, container.get()->totalFrameCount());
@@ -102,6 +105,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 118_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
+    info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
     jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(1, container.get()->totalFrameCount());
@@ -127,6 +131,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
+    info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
     jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -140,6 +145,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 137_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
+    info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
     jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(2, container.get()->totalFrameCount());
@@ -164,6 +170,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
+    info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
     jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -177,6 +184,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 137_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
+    info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
     jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -190,6 +198,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 169_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 168_ms;
+    info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
     jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(3, container.get()->totalFrameCount());
@@ -214,6 +223,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 117_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 116_ms;
+    info->set(FrameInfoIndex::WorkloadTarget) = 16_ms;
     jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -228,6 +238,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 133_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 132_ms;
+    info->set(FrameInfoIndex::WorkloadTarget) = 16_ms;
     jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -242,6 +253,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 165_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 148_ms;
+    info->set(FrameInfoIndex::WorkloadTarget) = 16_ms;
     jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(2, container.get()->jankFrameCount());
@@ -256,6 +268,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 181_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 164_ms;
+    info->set(FrameInfoIndex::WorkloadTarget) = 16_ms;
     jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(2, container.get()->jankFrameCount());
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index f09dc72..6b72173 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -25,8 +25,6 @@
     min_sdk_version: "",
 
     srcs: [
-        "android_media_ImageWriter.cpp",
-        "android_media_ImageReader.cpp",
         "android_media_JetPlayer.cpp",
         "android_media_MediaCrypto.cpp",
         "android_media_MediaCodec.cpp",
@@ -42,7 +40,6 @@
         "android_media_MediaProfiles.cpp",
         "android_media_MediaRecorder.cpp",
         "android_media_MediaSync.cpp",
-        "android_media_PublicFormatUtils.cpp",
         "android_media_ResampleInputStream.cpp",
         "android_media_Streams.cpp",
         "android_media_SyncParams.cpp",
@@ -64,7 +61,6 @@
         "libbinder",
         "libmedia",
         "libmedia_codeclist",
-        "libmedia_jni_utils",
         "libmedia_omx",
         "libmediametrics",
         "libmediadrm",
@@ -133,38 +129,6 @@
 }
 
 cc_library_shared {
-    name: "libmedia_jni_utils",
-    srcs: [
-        ":libgui_frame_event_aidl",
-        "android_media_Utils.cpp",
-    ],
-
-    header_libs: [
-        "libgui_headers",
-    ],
-
-    shared_libs: [
-        "liblog",
-        "libui",
-        "libutils",
-    ],
-
-    include_dirs: [
-        "system/media/camera/include",
-    ],
-
-    export_include_dirs: ["."],
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wno-error=deprecated-declarations",
-        "-Wunused",
-        "-Wunreachable-code",
-    ],
-}
-
-cc_library_shared {
     name: "libmedia_tv_tuner",
     min_sdk_version: "",
     srcs: [
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index a942300..647b553 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1476,8 +1476,6 @@
     return AndroidRuntime::registerNativeMethods(env,
                 "android/media/MediaPlayer", gMethods, NELEM(gMethods));
 }
-extern int register_android_media_ImageReader(JNIEnv *env);
-extern int register_android_media_ImageWriter(JNIEnv *env);
 extern int register_android_media_JetPlayer(JNIEnv *env);
 extern int register_android_media_Crypto(JNIEnv *env);
 extern int register_android_media_Drm(JNIEnv *env);
@@ -1490,7 +1488,6 @@
 extern int register_android_media_MediaMuxer(JNIEnv *env);
 extern int register_android_media_MediaRecorder(JNIEnv *env);
 extern int register_android_media_MediaSync(JNIEnv *env);
-extern int register_android_media_PublicFormatUtils(JNIEnv *env);
 extern int register_android_media_ResampleInputStream(JNIEnv *env);
 extern int register_android_media_MediaProfiles(JNIEnv *env);
 extern int register_android_mtp_MtpDatabase(JNIEnv *env);
@@ -1508,16 +1505,6 @@
     }
     assert(env != NULL);
 
-    if (register_android_media_ImageWriter(env) != JNI_OK) {
-        ALOGE("ERROR: ImageWriter native registration failed");
-        goto bail;
-    }
-
-    if (register_android_media_ImageReader(env) < 0) {
-        ALOGE("ERROR: ImageReader native registration failed");
-        goto bail;
-    }
-
     if (register_android_media_JetPlayer(env) < 0) {
         ALOGE("ERROR: JetPlayer native registration failed");
         goto bail;
@@ -1538,11 +1525,6 @@
         goto bail;
     }
 
-    if (register_android_media_PublicFormatUtils(env) < 0) {
-        ALOGE("ERROR: PublicFormatUtils native registration failed\n");
-        goto bail;
-    }
-
     if (register_android_media_ResampleInputStream(env) < 0) {
         ALOGE("ERROR: ResampleInputStream native registration failed\n");
         goto bail;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PreviewPageFrame.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PreviewPageFrame.java
index 95bdb09..e735610 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PreviewPageFrame.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PreviewPageFrame.java
@@ -16,6 +16,8 @@
 
 package com.android.printspooler.widget;
 
+import static android.view.accessibility.Flags.triStateChecked;
+
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.accessibility.AccessibilityEvent;
@@ -38,6 +40,20 @@
     }
 
     @Override
+    public boolean performClick() {
+        final boolean result = super.performClick();
+        // This widget is incorrectly using the notion of "selection"
+        // to represent checked state. We can't send this event in
+        // setSelected() because setSelected() is called when this widget
+        // is not attached.
+        if (triStateChecked()) {
+            notifyViewAccessibilityStateChangedIfNeeded(
+                    AccessibilityEvent.CONTENT_CHANGE_TYPE_CHECKED);
+        }
+        return result;
+    }
+
+    @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
         event.setChecked(isSelected());
@@ -48,6 +64,11 @@
         super.onInitializeAccessibilityNodeInfo(info);
         info.setSelected(false);
         info.setCheckable(true);
-        info.setChecked(isSelected());
+        if (triStateChecked()) {
+            info.setChecked(isSelected() ? AccessibilityNodeInfo.CHECKED_STATE_TRUE :
+                    AccessibilityNodeInfo.CHECKED_STATE_FALSE);
+        } else {
+            info.setChecked(isSelected());
+        }
     }
 }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/display/DisplayDensityUtilsTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/display/DisplayDensityUtilsTest.java
new file mode 100644
index 0000000..bba278a
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/display/DisplayDensityUtilsTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2025 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.settingslib.display;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
+import android.os.RemoteException;
+import android.view.Display;
+import android.view.DisplayAdjustments;
+import android.view.DisplayInfo;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settingslib.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class DisplayDensityUtilsTest {
+
+    private static final float MAX_SCALE = 1.33f;
+    private static final float MIN_SCALE = 0.85f;
+    private static final float MIN_INTERVAL = 0.09f;
+    @Mock
+    private Context mContext;
+    @Mock
+    private Resources mResources;
+    @Mock
+    private DisplayManager mDisplayManager;
+    @Mock
+    private DisplayManagerGlobal mDisplayManagerGlobal;
+    @Mock
+    private IWindowManager mIWindowManager;
+    private IWindowManager mWindowManagerToRestore;
+    private DisplayDensityUtils mDisplayDensityUtils;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mDisplayManager).when(mContext).getSystemService((Class<Object>) any());
+        mWindowManagerToRestore = WindowManagerGlobal.getWindowManagerService();
+        WindowManagerGlobal.setWindowManagerServiceForSystemProcess(mIWindowManager);
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getFraction(R.fraction.display_density_max_scale, 1, 1)).thenReturn(
+                MAX_SCALE);
+        when(mResources.getFraction(R.fraction.display_density_min_scale, 1, 1)).thenReturn(
+                MIN_SCALE);
+        when(mResources.getFraction(R.fraction.display_density_min_scale_interval, 1,
+                1)).thenReturn(MIN_INTERVAL);
+        when(mResources.getString(anyInt())).thenReturn("summary");
+    }
+
+    @After
+    public void teardown() {
+        WindowManagerGlobal.setWindowManagerServiceForSystemProcess(mWindowManagerToRestore);
+    }
+
+    @Test
+    public void createDisplayDensityUtil_onlyDefaultDisplay() throws RemoteException {
+        var info = createDisplayInfoForDisplay(Display.DEFAULT_DISPLAY, Display.TYPE_INTERNAL, 2560,
+                1600, 320);
+        var display = new Display(mDisplayManagerGlobal, info.displayId, info,
+                (DisplayAdjustments) null);
+        doReturn(new Display[]{display}).when(mDisplayManager).getDisplays(any());
+        doReturn(display).when(mDisplayManager).getDisplay(info.displayId);
+
+        mDisplayDensityUtils = new DisplayDensityUtils(mContext);
+
+        assertThat(mDisplayDensityUtils.getValues()).isEqualTo(new int[]{272, 320, 354, 390, 424});
+    }
+
+    @Test
+    public void createDisplayDensityUtil_multipleInternalDisplays() throws RemoteException {
+        // Default display
+        var defaultDisplayInfo = createDisplayInfoForDisplay(Display.DEFAULT_DISPLAY,
+                Display.TYPE_INTERNAL, 2000, 2000, 390);
+        var defaultDisplay = new Display(mDisplayManagerGlobal, defaultDisplayInfo.displayId,
+                defaultDisplayInfo,
+                (DisplayAdjustments) null);
+        doReturn(defaultDisplay).when(mDisplayManager).getDisplay(defaultDisplayInfo.displayId);
+
+        // Create another internal display
+        var internalDisplayInfo = createDisplayInfoForDisplay(1, Display.TYPE_INTERNAL,
+                2000, 1000, 390);
+        var internalDisplay = new Display(mDisplayManagerGlobal, internalDisplayInfo.displayId,
+                internalDisplayInfo,
+                (DisplayAdjustments) null);
+        doReturn(internalDisplay).when(mDisplayManager).getDisplay(internalDisplayInfo.displayId);
+
+        doReturn(new Display[]{defaultDisplay, internalDisplay}).when(mDisplayManager).getDisplays(
+                anyString());
+
+        mDisplayDensityUtils = new DisplayDensityUtils(mContext);
+
+        assertThat(mDisplayDensityUtils.getValues()).isEqualTo(new int[]{330, 390, 426, 462, 500});
+    }
+
+    private DisplayInfo createDisplayInfoForDisplay(int displayId, int displayType,
+            int width, int height, int density) throws RemoteException {
+        var displayInfo = new DisplayInfo();
+        displayInfo.displayId = displayId;
+        displayInfo.type = displayType;
+        displayInfo.logicalWidth = width;
+        displayInfo.logicalHeight = height;
+        displayInfo.logicalDensityDpi = density;
+
+        doReturn(displayInfo).when(mDisplayManagerGlobal).getDisplayInfo(displayInfo.displayId);
+        doReturn(displayInfo.logicalDensityDpi).when(mIWindowManager).getInitialDisplayDensity(
+                displayInfo.displayId);
+        return displayInfo;
+    }
+}
+
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 418a7a5..835dd7a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -76,7 +76,7 @@
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.selection.selectable
 import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.foundation.text.AutoSize
+import androidx.compose.foundation.text.TextAutoSize
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.icons.Icons
@@ -1045,7 +1045,7 @@
                         color = colors.onPrimary,
                         textAlign = TextAlign.Center,
                     ),
-                autoSize = AutoSize.StepBased(maxFontSize = 36.sp, stepSize = 0.1.sp),
+                autoSize = TextAutoSize.StepBased(maxFontSize = 36.sp, stepSize = 0.1.sp),
                 modifier =
                     Modifier.focusable().semantics(mergeDescendants = true) {
                         contentDescription = titleForEmptyStateCTA
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
index 16002bc..8ad96a5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
@@ -57,7 +57,8 @@
 internal constructor(
     communalContent: List<CommunalContentModel>,
     private val onAddWidget: (componentName: ComponentName, user: UserHandle, rank: Int) -> Unit,
-    private val onDeleteWidget: (id: Int, componentName: ComponentName, rank: Int) -> Unit,
+    private val onDeleteWidget:
+        (id: Int, key: String, componentName: ComponentName, rank: Int) -> Unit,
     private val onReorderWidgets: (widgetIdToRankMap: Map<Int, Int>) -> Unit,
     private val onResizeWidget:
         (
@@ -81,7 +82,7 @@
         if (list[indexToRemove].isWidgetContent()) {
             val widget = list[indexToRemove] as CommunalContentModel.WidgetContent
             list.apply { removeAt(indexToRemove) }
-            onDeleteWidget(widget.appWidgetId, widget.componentName, widget.rank)
+            onDeleteWidget(widget.appWidgetId, widget.key, widget.componentName, widget.rank)
         }
     }
 
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 aa0d474..0c502e6 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
@@ -109,6 +109,13 @@
         rememberMutableSceneTransitionLayoutState(
             initialScene = initialSceneKey,
             canChangeScene = { toScene -> viewModel.canChangeScene(toScene) },
+            canShowOverlay = { overlay -> viewModel.canShowOrReplaceOverlay(overlay) },
+            canReplaceOverlay = { beingReplaced, newlyShown ->
+                viewModel.canShowOrReplaceOverlay(
+                    newlyShown = newlyShown,
+                    beingReplaced = beingReplaced,
+                )
+            },
             transitions = sceneTransitions,
             onTransitionStart = { transition ->
                 sceneJankMonitor.onTransitionStart(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index 6f2082b..7051f81 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -24,53 +24,47 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.uiEventLoggerFake
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
-import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
 import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
+import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
 import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.communal.shared.model.EditModeState
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dock.dockManager
-import com.android.systemui.dock.fakeDockManager
 import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.notificationShadeWindowController
-import com.android.systemui.statusbar.phone.centralSurfaces
-import com.android.systemui.statusbar.phone.centralSurfacesOptional
 import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.kotlin.whenever
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
 @EnableFlags(FLAG_COMMUNAL_HUB)
@@ -82,7 +76,8 @@
         @JvmStatic
         @Parameters(name = "{0}")
         fun getParams(): List<FlagsParameterization> {
-            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+            return FlagsParameterization.allCombinationsOf(FLAG_GLANCEABLE_HUB_V2)
+                .andSceneContainer()
         }
     }
 
@@ -90,9 +85,22 @@
         mSetFlagsRule.setFlagsParameterization(flags)
     }
 
-    private val kosmos = testKosmos()
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
 
-    private lateinit var underTest: CommunalSceneStartable
+    private val Kosmos.underTest by
+        Kosmos.Fixture {
+            CommunalSceneStartable(
+                communalInteractor = communalInteractor,
+                communalSettingsInteractor = communalSettingsInteractor,
+                communalSceneInteractor = communalSceneInteractor,
+                keyguardInteractor = keyguardInteractor,
+                systemSettings = fakeSettings,
+                notificationShadeWindowController = notificationShadeWindowController,
+                bgScope = applicationCoroutineScope,
+                mainDispatcher = testDispatcher,
+                uiEventLogger = uiEventLoggerFake,
+            )
+        }
 
     @Before
     fun setUp() {
@@ -102,646 +110,314 @@
                 SCREEN_TIMEOUT,
                 UserHandle.USER_CURRENT,
             )
-            kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+            fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
 
-            underTest =
-                CommunalSceneStartable(
-                        dockManager = dockManager,
-                        communalInteractor = communalInteractor,
-                        communalSettingsInteractor = communalSettingsInteractor,
-                        communalSceneInteractor = communalSceneInteractor,
-                        keyguardTransitionInteractor = keyguardTransitionInteractor,
-                        keyguardInteractor = keyguardInteractor,
-                        systemSettings = fakeSettings,
-                        notificationShadeWindowController = notificationShadeWindowController,
-                        applicationScope = applicationCoroutineScope,
-                        bgScope = applicationCoroutineScope,
-                        mainDispatcher = testDispatcher,
-                        centralSurfacesOpt = centralSurfacesOptional,
-                        uiEventLogger = uiEventLoggerFake,
-                    )
-                    .apply { start() }
+            underTest.start()
 
             // Make communal available so that communalInteractor.desiredScene accurately reflects
             // scene changes instead of just returning Blank.
-            with(kosmos.testScope) {
-                launch { setCommunalAvailable(true) }
-                testScheduler.runCurrent()
-            }
+            runBlocking { setCommunalAvailable(true) }
+            setCommunalV2ConfigEnabled(true)
         }
     }
 
     @Test
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
-    fun keyguardGoesAway_whenLaunchingEditMode_doNotForceBlankScene() =
-        with(kosmos) {
-            testScope.runTest {
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
-                communalSceneInteractor.setEditModeState(EditModeState.STARTING)
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.PRIMARY_BOUNCER,
-                    to = KeyguardState.GONE,
-                    testScope = this,
-                )
-
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-            }
-        }
-
-    @Test
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
-    fun keyguardGoesAway_whenLaunchingWidget_doNotForceBlankScene() =
-        with(kosmos) {
-            testScope.runTest {
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
-                communalSceneInteractor.setIsLaunchingWidget(true)
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.PRIMARY_BOUNCER,
-                    to = KeyguardState.GONE,
-                    testScope = this,
-                )
-
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-            }
-        }
-
-    @Test
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
-    fun keyguardGoesAway_whenNotLaunchingWidget_forceBlankScene() =
-        with(kosmos) {
-            testScope.runTest {
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
-                communalSceneInteractor.setIsLaunchingWidget(false)
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.PRIMARY_BOUNCER,
-                    to = KeyguardState.GONE,
-                    testScope = this,
-                )
-
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-            }
-        }
-
-    @Test
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
-    fun keyguardGoesAway_whenInEditMode_doesNotChangeScene() =
-        with(kosmos) {
-            testScope.runTest {
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
-                communalInteractor.setEditModeOpen(true)
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.ALTERNATE_BOUNCER,
-                    to = KeyguardState.GONE,
-                    testScope = this,
-                )
-                // Scene change will be handled in EditWidgetsActivity not here
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-            }
-        }
-
-    @Ignore("Ignored until custom animations are implemented in b/322787129")
-    @Test
-    @DisableFlags(FLAG_SCENE_CONTAINER)
-    fun deviceDocked_forceCommunalScene() =
-        with(kosmos) {
-            testScope.runTest {
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-
-                updateDocked(true)
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.LOCKSCREEN,
-                    testScope = this,
-                )
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-            }
-        }
-
-    @Test
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
-    fun occluded_forceBlankScene() =
-        with(kosmos) {
-            testScope.runTest {
-                whenever(centralSurfaces.isLaunchingActivityOverLockscreen).thenReturn(false)
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
-                updateDocked(true)
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.OCCLUDED,
-                    testScope = this,
-                )
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-            }
-        }
-
-    @Test
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
-    fun occluded_doesNotForceBlankSceneIfLaunchingActivityOverLockscreen() =
-        with(kosmos) {
-            testScope.runTest {
-                whenever(centralSurfaces.isLaunchingActivityOverLockscreen).thenReturn(true)
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
-                updateDocked(true)
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.OCCLUDED,
-                    testScope = this,
-                )
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-            }
-        }
-
-    @Test
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
-    fun deviceDocked_doesNotForceCommunalIfTransitioningFromCommunal() =
-        with(kosmos) {
-            testScope.runTest {
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-
-                updateDocked(true)
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.LOCKSCREEN,
-                    testScope = this,
-                )
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-            }
-        }
-
-    @Test
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
-    fun deviceAsleep_forceBlankSceneAfterTimeout() =
-        with(kosmos) {
-            testScope.runTest {
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.OFF,
-                    testScope = this,
-                )
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
-                advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY)
-
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-            }
-        }
-
-    @Test
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
-    fun deviceAsleep_wakesUpBeforeTimeout_noChangeInScene() =
-        with(kosmos) {
-            testScope.runTest {
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.OFF,
-                    testScope = this,
-                )
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-                advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY / 2)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.OFF,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    testScope = this,
-                )
-
-                advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-            }
-        }
-
-    @Ignore("Ignored until custom animations are implemented in b/322787129")
-    @Test
-    @DisableFlags(FLAG_SCENE_CONTAINER)
-    fun dockingOnLockscreen_forcesCommunal() =
-        with(kosmos) {
-            testScope.runTest {
-                communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-
-                // device is docked while on the lockscreen
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.LOCKSCREEN,
-                    testScope = this,
-                )
-                updateDocked(true)
-
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-                advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-            }
-        }
-
-    @Ignore("Ignored until custom animations are implemented in b/322787129")
-    @Test
-    @DisableFlags(FLAG_SCENE_CONTAINER)
-    fun dockingOnLockscreen_doesNotForceCommunalIfDreamStarts() =
-        with(kosmos) {
-            testScope.runTest {
-                communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-
-                // device is docked while on the lockscreen
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.LOCKSCREEN,
-                    testScope = this,
-                )
-                updateDocked(true)
-
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-                advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY / 2)
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-
-                // dream starts shortly after docking
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.DREAMING,
-                    testScope = this,
-                )
-                advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY)
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-            }
-        }
-
-    @Test
-    @DisableFlags(FLAG_SCENE_CONTAINER)
+    @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
     fun hubTimeout_whenDreaming_goesToBlank() =
-        with(kosmos) {
-            testScope.runTest {
-                // Device is dreaming and on communal.
-                updateDreaming(true)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+        kosmos.runTest {
+            // Device is dreaming and on communal.
+            fakeKeyguardRepository.setDreaming(true)
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
 
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+            val scene by collectLastValue(communalSceneInteractor.currentScene)
+            assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
-                // Scene times out back to blank after the screen timeout.
-                advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-            }
+            // Scene times out back to blank after the screen timeout.
+            advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+            assertThat(scene).isEqualTo(CommunalScenes.Blank)
         }
 
     @Test
-    @DisableFlags(FLAG_SCENE_CONTAINER)
+    @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
     fun hubTimeout_notDreaming_staysOnCommunal() =
-        with(kosmos) {
-            testScope.runTest {
-                // Device is not dreaming and on communal.
-                updateDreaming(false)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+        kosmos.runTest {
+            // Device is not dreaming and on communal.
+            fakeKeyguardRepository.setDreaming(false)
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
 
-                // Scene stays as Communal
-                advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-            }
+            // Scene stays as Communal
+            advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+            val scene by collectLastValue(communalSceneInteractor.currentScene)
+            assertThat(scene).isEqualTo(CommunalScenes.Communal)
         }
 
     @Test
-    @DisableFlags(FLAG_SCENE_CONTAINER)
+    @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
     fun hubTimeout_dreamStopped_staysOnCommunal() =
-        with(kosmos) {
-            testScope.runTest {
-                // Device is dreaming and on communal.
-                updateDreaming(true)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+        kosmos.runTest {
+            // Device is dreaming and on communal.
+            fakeKeyguardRepository.setDreaming(true)
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
 
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+            val scene by collectLastValue(communalSceneInteractor.currentScene)
+            assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
-                // Wait a bit, but not long enough to timeout.
-                advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+            // Wait a bit, but not long enough to timeout.
+            advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+            assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
-                // Dream stops, timeout is cancelled and device stays on hub, because the regular
-                // screen timeout will take effect at this point.
-                updateDreaming(false)
-                advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-            }
+            // Dream stops, timeout is cancelled and device stays on hub, because the regular
+            // screen timeout will take effect at this point.
+            fakeKeyguardRepository.setDreaming(false)
+            advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+            assertThat(scene).isEqualTo(CommunalScenes.Communal)
         }
 
     @Test
-    @DisableFlags(FLAG_SCENE_CONTAINER)
+    @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
     fun hubTimeout_dreamStartedHalfway_goesToCommunal() =
-        with(kosmos) {
-            testScope.runTest {
-                // Device is on communal, but not dreaming.
-                updateDreaming(false)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+        kosmos.runTest {
+            // Device is on communal, but not dreaming.
+            fakeKeyguardRepository.setDreaming(false)
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
 
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+            val scene by collectLastValue(communalSceneInteractor.currentScene)
+            assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
-                // Wait a bit, but not long enough to timeout, then start dreaming.
-                advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
-                updateDreaming(true)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+            // Wait a bit, but not long enough to timeout, then start dreaming.
+            advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+            fakeKeyguardRepository.setDreaming(true)
+            assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
-                // Device times out after one screen timeout interval, dream doesn't reset timeout.
-                advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-            }
+            // Device times out after one screen timeout interval, dream doesn't reset timeout.
+            advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+            assertThat(scene).isEqualTo(CommunalScenes.Blank)
         }
 
     @Test
-    @DisableFlags(FLAG_SCENE_CONTAINER)
+    @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
     fun hubTimeout_dreamAfterInitialTimeout_goesToBlank() =
-        with(kosmos) {
-            testScope.runTest {
-                // Device is on communal.
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+        kosmos.runTest {
+            // Device is on communal.
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
 
-                // Device stays on the hub after the timeout since we're not dreaming.
-                advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2)
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+            // Device stays on the hub after the timeout since we're not dreaming.
+            testScope.advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2)
+            val scene by collectLastValue(communalSceneInteractor.currentScene)
+            assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
-                // Start dreaming.
-                updateDreaming(true)
-                advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
+            // Start dreaming.
+            fakeKeyguardRepository.setDreaming(true)
+            advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS.milliseconds)
 
-                // Hub times out immediately.
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-            }
+            // Hub times out immediately.
+            assertThat(scene).isEqualTo(CommunalScenes.Blank)
         }
 
     @Test
-    @DisableFlags(FLAG_SCENE_CONTAINER)
+    @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
     fun hubTimeout_userActivityTriggered_resetsTimeout() =
-        with(kosmos) {
-            testScope.runTest {
-                // Device is dreaming and on communal.
-                updateDreaming(true)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+        kosmos.runTest {
+            // Device is dreaming and on communal.
+            fakeKeyguardRepository.setDreaming(true)
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
 
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+            val scene by collectLastValue(communalSceneInteractor.currentScene)
+            assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
-                // Wait a bit, but not long enough to timeout.
-                advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+            // Wait a bit, but not long enough to timeout.
+            advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
 
-                // Send user interaction to reset timeout.
-                communalInteractor.signalUserInteraction()
+            // Send user interaction to reset timeout.
+            communalInteractor.signalUserInteraction()
 
-                // If user activity didn't reset timeout, we would have gone back to Blank by now.
-                advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+            // If user activity didn't reset timeout, we would have gone back to Blank by now.
+            advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+            assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
-                // Timeout happens one interval after the user interaction.
-                advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-            }
+            // Timeout happens one interval after the user interaction.
+            advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+            assertThat(scene).isEqualTo(CommunalScenes.Blank)
         }
 
     @Test
-    @DisableFlags(FLAG_SCENE_CONTAINER)
+    @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
     fun hubTimeout_screenTimeoutChanged() =
-        with(kosmos) {
-            testScope.runTest {
-                fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT * 2)
+        kosmos.runTest {
+            fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT * 2)
 
-                // Device is dreaming and on communal.
-                updateDreaming(true)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+            // Device is dreaming and on communal.
+            fakeKeyguardRepository.setDreaming(true)
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
 
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+            val scene by collectLastValue(communalSceneInteractor.currentScene)
+            assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
-                // Scene times out back to blank after the screen timeout.
-                advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+            // Scene times out back to blank after the screen timeout.
+            advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+            assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
-                advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-                assertThat(uiEventLoggerFake.logs.first().eventId)
-                    .isEqualTo(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT.id)
-                assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
-            }
+            advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+            assertThat(scene).isEqualTo(CommunalScenes.Blank)
+            assertThat(uiEventLoggerFake.logs.first().eventId)
+                .isEqualTo(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT.id)
+            assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
         }
 
     @Test
     @EnableFlags(FLAG_SCENE_CONTAINER)
+    @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
     fun hubTimeout_withSceneContainer_whenDreaming_goesToBlank() =
-        with(kosmos) {
-            testScope.runTest {
-                // Device is dreaming and on communal.
-                updateDreaming(true)
-                sceneInteractor.changeScene(Scenes.Communal, "test")
+        kosmos.runTest {
+            // Device is dreaming and on communal.
+            fakeKeyguardRepository.setDreaming(true)
+            sceneInteractor.changeScene(Scenes.Communal, "test")
 
-                val scene by collectLastValue(sceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(Scenes.Communal)
+            val scene by collectLastValue(sceneInteractor.currentScene)
+            assertThat(scene).isEqualTo(Scenes.Communal)
 
-                // Scene times out back to blank after the screen timeout.
-                advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
-                assertThat(scene).isEqualTo(Scenes.Dream)
-            }
+            // Scene times out back to blank after the screen timeout.
+            advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+            assertThat(scene).isEqualTo(Scenes.Dream)
         }
 
     @Test
     @EnableFlags(FLAG_SCENE_CONTAINER)
+    @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
     fun hubTimeout_withSceneContainer_notDreaming_staysOnCommunal() =
-        with(kosmos) {
-            testScope.runTest {
-                // Device is not dreaming and on communal.
-                updateDreaming(false)
-                sceneInteractor.changeScene(Scenes.Communal, "test")
+        kosmos.runTest {
+            // Device is not dreaming and on communal.
+            fakeKeyguardRepository.setDreaming(false)
+            sceneInteractor.changeScene(Scenes.Communal, "test")
 
-                // Scene stays as Communal
-                advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
-                val scene by collectLastValue(sceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(Scenes.Communal)
-            }
+            // Scene stays as Communal
+            advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+            val scene by collectLastValue(sceneInteractor.currentScene)
+            assertThat(scene).isEqualTo(Scenes.Communal)
         }
 
     @Test
     @EnableFlags(FLAG_SCENE_CONTAINER)
+    @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
     fun hubTimeout_withSceneContainer_dreamStopped_staysOnCommunal() =
-        with(kosmos) {
-            testScope.runTest {
-                // Device is dreaming and on communal.
-                updateDreaming(true)
-                sceneInteractor.changeScene(Scenes.Communal, "test")
+        kosmos.runTest {
+            // Device is dreaming and on communal.
+            fakeKeyguardRepository.setDreaming(true)
+            sceneInteractor.changeScene(Scenes.Communal, "test")
 
-                val scene by collectLastValue(sceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(Scenes.Communal)
+            val scene by collectLastValue(sceneInteractor.currentScene)
+            assertThat(scene).isEqualTo(Scenes.Communal)
 
-                // Wait a bit, but not long enough to timeout.
-                advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
-                assertThat(scene).isEqualTo(Scenes.Communal)
+            // Wait a bit, but not long enough to timeout.
+            advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+            assertThat(scene).isEqualTo(Scenes.Communal)
 
-                // Dream stops, timeout is cancelled and device stays on hub, because the regular
-                // screen timeout will take effect at this point.
-                updateDreaming(false)
-                advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
-                assertThat(scene).isEqualTo(Scenes.Communal)
-            }
+            // Dream stops, timeout is cancelled and device stays on hub, because the regular
+            // screen timeout will take effect at this point.
+            fakeKeyguardRepository.setDreaming(false)
+            advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+            assertThat(scene).isEqualTo(Scenes.Communal)
         }
 
     @Test
     @EnableFlags(FLAG_SCENE_CONTAINER)
+    @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
     fun hubTimeout_withSceneContainer_dreamStartedHalfway_goesToCommunal() =
-        with(kosmos) {
-            testScope.runTest {
-                // Device is on communal, but not dreaming.
-                updateDreaming(false)
-                sceneInteractor.changeScene(Scenes.Communal, "test")
+        kosmos.runTest {
+            // Device is on communal, but not dreaming.
+            fakeKeyguardRepository.setDreaming(false)
+            sceneInteractor.changeScene(Scenes.Communal, "test")
 
-                val scene by collectLastValue(sceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(Scenes.Communal)
+            val scene by collectLastValue(sceneInteractor.currentScene)
+            assertThat(scene).isEqualTo(Scenes.Communal)
 
-                // Wait a bit, but not long enough to timeout, then start dreaming.
-                advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
-                updateDreaming(true)
-                assertThat(scene).isEqualTo(Scenes.Communal)
+            // Wait a bit, but not long enough to timeout, then start dreaming.
+            advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+            fakeKeyguardRepository.setDreaming(true)
+            assertThat(scene).isEqualTo(Scenes.Communal)
 
-                // Device times out after one screen timeout interval, dream doesn't reset timeout.
-                advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
-                assertThat(scene).isEqualTo(Scenes.Dream)
-            }
+            // Device times out after one screen timeout interval, dream doesn't reset timeout.
+            advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+            assertThat(scene).isEqualTo(Scenes.Dream)
         }
 
     @Test
     @EnableFlags(FLAG_SCENE_CONTAINER)
+    @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
     fun hubTimeout_withSceneContainer_dreamAfterInitialTimeout_goesToBlank() =
-        with(kosmos) {
-            testScope.runTest {
-                // Device is on communal.
-                sceneInteractor.changeScene(Scenes.Communal, "test")
+        kosmos.runTest {
+            // Device is on communal.
+            sceneInteractor.changeScene(Scenes.Communal, "test")
 
-                // Device stays on the hub after the timeout since we're not dreaming.
-                advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2)
-                val scene by collectLastValue(sceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(Scenes.Communal)
+            // Device stays on the hub after the timeout since we're not dreaming.
+            advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2)
+            val scene by collectLastValue(sceneInteractor.currentScene)
+            assertThat(scene).isEqualTo(Scenes.Communal)
 
-                // Start dreaming.
-                updateDreaming(true)
-                advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
+            // Start dreaming.
+            fakeKeyguardRepository.setDreaming(true)
+            advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS.milliseconds)
 
-                // Hub times out immediately.
-                assertThat(scene).isEqualTo(Scenes.Dream)
-            }
+            // Hub times out immediately.
+            assertThat(scene).isEqualTo(Scenes.Dream)
         }
 
     @Test
     @EnableFlags(FLAG_SCENE_CONTAINER)
+    @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
     fun hubTimeout_withSceneContainer_userActivityTriggered_resetsTimeout() =
-        with(kosmos) {
-            testScope.runTest {
-                // Device is dreaming and on communal.
-                updateDreaming(true)
-                sceneInteractor.changeScene(Scenes.Communal, "test")
+        kosmos.runTest {
+            // Device is dreaming and on communal.
+            fakeKeyguardRepository.setDreaming(true)
+            sceneInteractor.changeScene(Scenes.Communal, "test")
 
-                val scene by collectLastValue(sceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(Scenes.Communal)
+            val scene by collectLastValue(sceneInteractor.currentScene)
+            assertThat(scene).isEqualTo(Scenes.Communal)
 
-                // Wait a bit, but not long enough to timeout.
-                advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+            // Wait a bit, but not long enough to timeout.
+            advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
 
-                // Send user interaction to reset timeout.
-                communalInteractor.signalUserInteraction()
+            // Send user interaction to reset timeout.
+            communalInteractor.signalUserInteraction()
 
-                // If user activity didn't reset timeout, we would have gone back to Blank by now.
-                advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
-                assertThat(scene).isEqualTo(Scenes.Communal)
+            // If user activity didn't reset timeout, we would have gone back to Blank by now.
+            advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+            assertThat(scene).isEqualTo(Scenes.Communal)
 
-                // Timeout happens one interval after the user interaction.
-                advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
-                assertThat(scene).isEqualTo(Scenes.Dream)
-            }
+            // Timeout happens one interval after the user interaction.
+            advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+            assertThat(scene).isEqualTo(Scenes.Dream)
         }
 
     @Test
     @EnableFlags(FLAG_SCENE_CONTAINER)
+    @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
     fun hubTimeout_withSceneContainer_screenTimeoutChanged() =
-        with(kosmos) {
-            testScope.runTest {
-                fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT * 2)
+        kosmos.runTest {
+            fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT * 2)
 
-                // Device is dreaming and on communal.
-                updateDreaming(true)
-                sceneInteractor.changeScene(Scenes.Communal, "test")
+            // Device is dreaming and on communal.
+            fakeKeyguardRepository.setDreaming(true)
+            sceneInteractor.changeScene(Scenes.Communal, "test")
 
-                val scene by collectLastValue(sceneInteractor.currentScene)
-                assertThat(scene).isEqualTo(Scenes.Communal)
+            val scene by collectLastValue(sceneInteractor.currentScene)
+            assertThat(scene).isEqualTo(Scenes.Communal)
 
-                // Scene times out back to blank after the screen timeout.
-                advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
-                assertThat(scene).isEqualTo(Scenes.Communal)
+            // Scene times out back to blank after the screen timeout.
+            advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+            assertThat(scene).isEqualTo(Scenes.Communal)
 
-                advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
-                assertThat(scene).isEqualTo(Scenes.Dream)
-                assertThat(uiEventLoggerFake.logs.first().eventId)
-                    .isEqualTo(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT.id)
-                assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
-            }
+            advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+            assertThat(scene).isEqualTo(Scenes.Dream)
+            assertThat(uiEventLoggerFake.logs.first().eventId)
+                .isEqualTo(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT.id)
+            assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
         }
 
-    @Test
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
-    fun transitionFromDozingToGlanceableHub_forcesCommunal() =
-        with(kosmos) {
-            testScope.runTest {
-                val scene by collectLastValue(communalSceneInteractor.currentScene)
-                communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
-                assertThat(scene).isEqualTo(CommunalScenes.Blank)
-
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.DOZING,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    testScope = this,
-                )
-
-                assertThat(scene).isEqualTo(CommunalScenes.Communal)
-            }
-        }
-
-    private fun TestScope.updateDocked(docked: Boolean) =
-        with(kosmos) {
-            runCurrent()
-            fakeDockManager.setIsDocked(docked)
-            // TODO(b/322787129): uncomment once custom animations are in place
-            // fakeDockManager.setDockEvent(DockManager.STATE_DOCKED)
-            runCurrent()
-        }
-
-    private fun TestScope.updateDreaming(dreaming: Boolean) =
-        with(kosmos) {
-            fakeKeyguardRepository.setDreaming(dreaming)
-            runCurrent()
-        }
+    /**
+     * Advances time by duration + 1 millisecond, to ensure that tasks scheduled to run at
+     * currentTime + duration are scheduled.
+     */
+    private fun Kosmos.advanceTimeBy(duration: Duration) =
+        testScope.advanceTimeBy(duration + 1.milliseconds)
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
index ff722bf..aa96073 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
@@ -22,7 +22,6 @@
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.ObservableTransitionState.Idle
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
-import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.communalSceneRepository
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -64,7 +63,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@EnableFlags(FLAG_COMMUNAL_HUB, FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+@EnableFlags(FLAG_COMMUNAL_HUB)
 @DisableSceneContainer
 class CommunalSceneTransitionInteractorTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 522650b..f5f5dd8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -201,6 +201,7 @@
 
             underTest.onDeleteWidget(
                 id = 0,
+                key = "key_0",
                 componentName = ComponentName("test_package", "test_class"),
                 rank = 30,
             )
@@ -214,6 +215,24 @@
         }
 
     @Test
+    fun deleteWidget_clearsSelectedKey() =
+        kosmos.runTest {
+            val selectedKey by collectLastValue(underTest.selectedKey)
+            underTest.setSelectedKey("test_key")
+            assertThat(selectedKey).isEqualTo("test_key")
+
+            // Selected key is deleted.
+            underTest.onDeleteWidget(
+                id = 0,
+                key = "test_key",
+                componentName = ComponentName("test_package", "test_class"),
+                rank = 30,
+            )
+
+            assertThat(selectedKey).isNull()
+        }
+
+    @Test
     fun reorderWidget_uiEventLogging_start() =
         kosmos.runTest {
             underTest.onReorderWidgetStart(CommunalContentModel.KEY.widget(123))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index 7e93f5a..9be786f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -25,7 +25,6 @@
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
-import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
 import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
 import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
 import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
@@ -39,7 +38,6 @@
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
 import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
 import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
 import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
@@ -70,7 +68,6 @@
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -103,10 +100,7 @@
         @JvmStatic
         @Parameters(name = "{0}")
         fun getParams(): List<FlagsParameterization> {
-            return FlagsParameterization.allCombinationsOf(
-                FLAG_COMMUNAL_SCENE_KTF_REFACTOR,
-                FLAG_GLANCEABLE_HUB_V2,
-            )
+            return FlagsParameterization.allCombinationsOf(FLAG_GLANCEABLE_HUB_V2)
         }
     }
 
@@ -174,22 +168,6 @@
 
     @Test
     @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_GLANCEABLE_HUB_V2)
-    fun testTransitionToLockscreen_onWake_canDream_glanceableHubAvailable() =
-        kosmos.runTest {
-            whenever(dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
-            setCommunalAvailable(true)
-
-            powerInteractor.setAwakeForTest()
-
-            // If dreaming is possible and communal is available, then we should transition to
-            // GLANCEABLE_HUB when waking up due to power button press.
-            assertThat(transitionRepository)
-                .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.GLANCEABLE_HUB)
-        }
-
-    @Test
-    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR, FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun testTransitionToLockscreen_onWake_canDream_ktfRefactor() =
         kosmos.runTest {
             setCommunalAvailable(true)
@@ -243,24 +221,7 @@
         }
 
     @Test
-    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun testTransitionToGlanceableHub_onWakeup_ifIdleOnCommunal_noOccludingActivity() =
-        kosmos.runTest {
-            fakeCommunalSceneRepository.setTransitionState(
-                flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
-            )
-
-            powerInteractor.setAwakeForTest()
-
-            // Under default conditions, we should transition to LOCKSCREEN when waking up.
-            assertThat(transitionRepository)
-                .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.GLANCEABLE_HUB)
-        }
-
-    @Test
     @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR, FLAG_SCENE_CONTAINER)
-    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun testTransitionToGlanceableHub_onWakeup_ifAvailable() =
         kosmos.runTest {
             setCommunalAvailable(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
index 5882cff..898a5c1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
@@ -23,7 +23,6 @@
 import android.service.dream.dreamManager
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
 import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
 import com.android.systemui.Flags.glanceableHubV2
 import com.android.systemui.SysuiTestCase
@@ -74,10 +73,7 @@
         @JvmStatic
         @Parameters(name = "{0}")
         fun getParams(): List<FlagsParameterization> {
-            return FlagsParameterization.allCombinationsOf(
-                    FLAG_COMMUNAL_SCENE_KTF_REFACTOR,
-                    FLAG_GLANCEABLE_HUB_V2,
-                )
+            return FlagsParameterization.allCombinationsOf(FLAG_GLANCEABLE_HUB_V2)
                 .andSceneContainer()
         }
     }
@@ -202,7 +198,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
     fun testTransitionToGlanceableHubOnWake() =
         kosmos.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
index 63ed040..9a8e095 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
@@ -32,16 +32,11 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
-import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
-import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
 import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
@@ -51,7 +46,6 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.testKosmos
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -101,21 +95,4 @@
             assertThat(transitionRepository)
                 .startedTransition(from = KeyguardState.OCCLUDED, to = KeyguardState.LOCKSCREEN)
         }
-
-    @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun testShowWhenLockedActivity_noLongerOnTop_transitionsToGlanceableHub_ifIdleOnCommunal() =
-        testScope.runTest {
-            kosmos.fakeCommunalSceneRepository.setTransitionState(
-                flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
-            )
-            runCurrent()
-
-            kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = false)
-            runCurrent()
-
-            assertThat(transitionRepository)
-                .startedTransition(from = KeyguardState.OCCLUDED, to = KeyguardState.GLANCEABLE_HUB)
-        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index d057f7a0..a090fab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.app.StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
-import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
@@ -26,7 +25,6 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
 import com.android.systemui.Flags
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
-import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
 import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
 import com.android.systemui.Flags.glanceableHubV2
 import com.android.systemui.SysuiTestCase
@@ -135,8 +133,7 @@
         @JvmStatic
         @Parameters(name = "{0}")
         fun getParams(): List<FlagsParameterization> {
-            return FlagsParameterization.allCombinationsOf(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-                .andSceneContainer()
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
         }
     }
 
@@ -468,38 +465,6 @@
 
     @Test
     @DisableSceneContainer
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun dozingToGlanceableHub() =
-        testScope.runTest {
-            // GIVEN a prior transition has run to DOZING
-            runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.DOZING)
-            runCurrent()
-
-            // GIVEN the device is idle on the glanceable hub
-            val idleTransitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(CommunalScenes.Communal)
-                )
-            communalInteractor.setTransitionState(idleTransitionState)
-            runCurrent()
-
-            // WHEN the device begins to wake
-            keyguardRepository.setKeyguardShowing(true)
-            powerInteractor.setAwakeForTest()
-            advanceTimeBy(60L)
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    from = KeyguardState.DOZING,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    animatorAssertion = { it.isNotNull() },
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @DisableSceneContainer
     fun goneToDozing() =
         testScope.runTest {
             // GIVEN a device with AOD not available
@@ -625,40 +590,6 @@
 
     @Test
     @BrokenWithSceneContainer(339465026)
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun goneToGlanceableHub() =
-        testScope.runTest {
-            // GIVEN a prior transition has run to GONE
-            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
-
-            // GIVEN the device is idle on the glanceable hub
-            val idleTransitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(CommunalScenes.Communal)
-                )
-            communalInteractor.setTransitionState(idleTransitionState)
-            runCurrent()
-
-            // WHEN the keyguard starts to show
-            keyguardRepository.setKeyguardShowing(true)
-            runCurrent()
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    from = KeyguardState.GONE,
-                    ownerName =
-                        FromGoneTransitionInteractor::class.simpleName +
-                            "(keyguard interactor says keyguard is showing)",
-                    animatorAssertion = { it.isNotNull() },
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @BrokenWithSceneContainer(339465026)
-    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun goneToGlanceableHub_communalKtfRefactor() =
         testScope.runTest {
             // GIVEN a prior transition has run to GONE
@@ -1070,84 +1001,6 @@
 
     @Test
     @BrokenWithSceneContainer(339465026)
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun occludedToGlanceableHub() =
-        testScope.runTest {
-            // GIVEN a device on lockscreen
-            keyguardRepository.setKeyguardShowing(true)
-            runCurrent()
-
-            // GIVEN the device is idle on the glanceable hub
-            val idleTransitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(CommunalScenes.Communal)
-                )
-            communalInteractor.setTransitionState(idleTransitionState)
-            runCurrent()
-
-            // GIVEN a prior transition has run to OCCLUDED
-            runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.OCCLUDED)
-            keyguardRepository.setKeyguardOccluded(true)
-            runCurrent()
-
-            // WHEN occlusion ends
-            keyguardRepository.setKeyguardOccluded(false)
-            runCurrent()
-
-            // THEN a transition to GLANCEABLE_HUB should occur
-            assertThat(transitionRepository)
-                .startedTransition(
-                    ownerName = FromOccludedTransitionInteractor::class.simpleName,
-                    from = KeyguardState.OCCLUDED,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    animatorAssertion = { it.isNotNull() },
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @BrokenWithSceneContainer(339465026)
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun occludedToGlanceableHubWhenInitiallyOnHub() =
-        testScope.runTest {
-            // GIVEN a device on lockscreen and communal is available
-            keyguardRepository.setKeyguardShowing(true)
-            kosmos.setCommunalAvailable(true)
-            runCurrent()
-
-            // GIVEN a prior transition has run to OCCLUDED from GLANCEABLE_HUB
-            runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.OCCLUDED)
-            keyguardRepository.setKeyguardOccluded(true)
-            runCurrent()
-
-            // GIVEN on blank scene
-            val idleTransitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(CommunalScenes.Blank)
-                )
-            communalInteractor.setTransitionState(idleTransitionState)
-            runCurrent()
-
-            // WHEN occlusion ends
-            keyguardRepository.setKeyguardOccluded(false)
-            runCurrent()
-
-            // THEN a transition to GLANCEABLE_HUB should occur
-            assertThat(transitionRepository)
-                .startedTransition(
-                    ownerName = FromOccludedTransitionInteractor::class.simpleName,
-                    from = KeyguardState.OCCLUDED,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    animatorAssertion = { it.isNotNull() },
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @BrokenWithSceneContainer(339465026)
-    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun occludedToGlanceableHub_communalKtfRefactor() =
         testScope.runTest {
             // GIVEN a device on lockscreen and communal is available
@@ -1390,48 +1243,6 @@
 
     @Test
     @DisableSceneContainer
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun dreamingToGlanceableHub() =
-        testScope.runTest {
-            // GIVEN a prior transition has run to DREAMING
-            keyguardRepository.setDreaming(true)
-            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING)
-            runCurrent()
-
-            // WHEN a transition to the glanceable hub starts
-            val currentScene = CommunalScenes.Blank
-            val targetScene = CommunalScenes.Communal
-
-            val progress = MutableStateFlow(0f)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = currentScene,
-                        toScene = targetScene,
-                        currentScene = flowOf(targetScene),
-                        progress = progress,
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            communalInteractor.setTransitionState(transitionState)
-            progress.value = .1f
-            runCurrent()
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    ownerName = FromDreamingTransitionInteractor::class.simpleName,
-                    from = KeyguardState.DREAMING,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    animatorAssertion = { it.isNull() }, // transition should be manually animated
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @DisableSceneContainer
-    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun dreamingToGlanceableHub_communalKtfRefactor() =
         testScope.runTest {
             // GIVEN a prior transition has run to DREAMING
@@ -1596,66 +1407,6 @@
 
     @Test
     @DisableSceneContainer
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun lockscreenToGlanceableHub() =
-        testScope.runTest {
-            // GIVEN a prior transition has run to LOCKSCREEN
-            runTransitionAndSetWakefulness(KeyguardState.AOD, KeyguardState.LOCKSCREEN)
-            runCurrent()
-
-            // WHEN a glanceable hub transition starts
-            val currentScene = CommunalScenes.Blank
-            val targetScene = CommunalScenes.Communal
-
-            val progress = MutableStateFlow(0f)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = currentScene,
-                        toScene = targetScene,
-                        currentScene = flowOf(targetScene),
-                        progress = progress,
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            communalInteractor.setTransitionState(transitionState)
-            progress.value = .1f
-            runCurrent()
-
-            // THEN a transition from LOCKSCREEN => GLANCEABLE_HUB should occur
-            assertThat(transitionRepository)
-                .startedTransition(
-                    ownerName = FromLockscreenTransitionInteractor::class.simpleName,
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    animatorAssertion = { it.isNull() }, // transition should be manually animated
-                )
-
-            // WHEN the user stops dragging and the glanceable hub opening is cancelled
-            clearInvocations(transitionRepository)
-            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GLANCEABLE_HUB)
-            val idleTransitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(currentScene)
-                )
-            communalInteractor.setTransitionState(idleTransitionState)
-            runCurrent()
-
-            // THEN a transition from LOCKSCREEN => GLANCEABLE_HUB should occur
-            assertThat(transitionRepository)
-                .startedTransition(
-                    ownerName = FromLockscreenTransitionInteractor::class.simpleName,
-                    from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.LOCKSCREEN,
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @DisableSceneContainer
-    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun lockscreenToGlanceableHub_communalKtfRefactor() =
         testScope.runTest {
             // GIVEN a prior transition has run to LOCKSCREEN
@@ -1696,86 +1447,6 @@
 
     @Test
     @DisableSceneContainer
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun glanceableHubToLockscreen() =
-        testScope.runTest {
-            // GIVEN a prior transition has run to GLANCEABLE_HUB
-            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GLANCEABLE_HUB)
-            runCurrent()
-
-            // WHEN a transition away from glanceable hub starts
-            val currentScene = CommunalScenes.Communal
-            val targetScene = CommunalScenes.Blank
-
-            val progress = MutableStateFlow(0f)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = currentScene,
-                        toScene = targetScene,
-                        currentScene = flowOf(targetScene),
-                        progress = progress,
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            communalInteractor.setTransitionState(transitionState)
-            progress.value = .1f
-            runCurrent()
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
-                    from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.LOCKSCREEN,
-                    animatorAssertion = { it.isNull() }, // transition should be manually animated
-                )
-
-            // WHEN the user stops dragging and the glanceable hub closing is cancelled
-            clearInvocations(transitionRepository)
-            runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.LOCKSCREEN)
-            val idleTransitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(currentScene)
-                )
-            communalInteractor.setTransitionState(idleTransitionState)
-            runCurrent()
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun glanceableHubToDozing() =
-        testScope.runTest {
-            // GIVEN a prior transition has run to GLANCEABLE_HUB
-            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GLANCEABLE_HUB)
-
-            // WHEN the device begins to sleep
-            powerInteractor.setAsleepForTest()
-            runCurrent()
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
-                    from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.DOZING,
-                    animatorAssertion = { it.isNotNull() },
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @DisableSceneContainer
-    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToDozing_communalKtfRefactor() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1844,39 +1515,6 @@
 
     @Test
     @BrokenWithSceneContainer(339465026)
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun glanceableHubToOccluded() =
-        testScope.runTest {
-            // GIVEN a prior transition has run to GLANCEABLE_HUB
-            runTransitionAndSetWakefulness(KeyguardState.GONE, KeyguardState.GLANCEABLE_HUB)
-            runCurrent()
-
-            // GIVEN the device is idle on the glanceable hub
-            val idleTransitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(CommunalScenes.Communal)
-                )
-            communalInteractor.setTransitionState(idleTransitionState)
-            runCurrent()
-
-            // WHEN the keyguard is occluded
-            keyguardRepository.setKeyguardOccluded(true)
-            runCurrent()
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
-                    from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.OCCLUDED,
-                    animatorAssertion = { it.isNotNull() },
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @BrokenWithSceneContainer(339465026)
-    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToOccluded_communalKtfRefactor() =
         testScope.runTest {
             // GIVEN device is not dreaming
@@ -1905,30 +1543,6 @@
 
     @Test
     @DisableSceneContainer
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun glanceableHubToGone() =
-        testScope.runTest {
-            // GIVEN a prior transition has run to GLANCEABLE_HUB
-            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GLANCEABLE_HUB)
-
-            // WHEN keyguard goes away
-            keyguardRepository.setKeyguardGoingAway(true)
-            runCurrent()
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
-                    from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.GONE,
-                    animatorAssertion = { it.isNotNull() },
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @DisableSceneContainer
-    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToGone_communalKtfRefactor() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1953,7 +1567,7 @@
 
     @Test
     @DisableSceneContainer
-    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_GLANCEABLE_HUB_V2)
+    @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
     fun glanceableHubToDreaming_v2() =
         testScope.runTest {
             kosmos.setCommunalV2Enabled(true)
@@ -1985,55 +1599,7 @@
         }
 
     @Test
-    @DisableSceneContainer
-    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun glanceableHubToDreaming() =
-        testScope.runTest {
-            runCurrent()
-
-            // GIVEN that we are dreaming and not dozing
-            keyguardRepository.setDreaming(true)
-            keyguardRepository.setDozeTransitionModel(
-                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
-            )
-            advanceTimeBy(600L)
-
-            // GIVEN a prior transition has run to GLANCEABLE_HUB
-            runTransitionAndSetWakefulness(KeyguardState.DREAMING, KeyguardState.GLANCEABLE_HUB)
-            runCurrent()
-
-            // WHEN a transition away from glanceable hub starts
-            val currentScene = CommunalScenes.Communal
-            val targetScene = CommunalScenes.Blank
-
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = currentScene,
-                        toScene = targetScene,
-                        currentScene = flowOf(targetScene),
-                        progress = flowOf(0f, 0.1f),
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            communalSceneInteractor.setTransitionState(transitionState)
-            runCurrent()
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
-                    from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.DREAMING,
-                    animatorAssertion = { it.isNull() }, // transition should be manually animated
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
     @BrokenWithSceneContainer(339465026)
-    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToOccludedDoesNotTriggerWhenDreamStateChanges_communalKtfRefactor() =
         testScope.runTest {
             // GIVEN that we are dreaming and not dozing
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 847044a..95d9c8f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -386,6 +386,7 @@
         verify(mMediaSwitchingController).logInteractionAdjustVolume(mMediaDevice1);
     }
 
+    @DisableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING)
     @Test
     public void onBindViewHolder_bindSelectableDevice_verifyView() {
         List<MediaDevice> selectableDevices = new ArrayList<>();
@@ -396,10 +397,55 @@
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mCheckBox.isChecked()).isFalse();
         assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
         assertThat(mViewHolder.mContainerLayout.isFocusable()).isTrue();
+
+        mViewHolder.mCheckBox.performClick();
+        verify(mMediaSwitchingController).addDeviceToPlayMedia(mMediaDevice2);
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING)
+    @Test
+    public void onBindViewHolder_bindDeselectableDevice_verifyView() {
+        when(mMediaSwitchingController.getSelectedMediaDevice()).thenReturn(
+                List.of(mMediaDevice1, mMediaDevice2));
+        when(mMediaSwitchingController.getDeselectableMediaDevice()).thenReturn(
+                List.of(mMediaDevice1, mMediaDevice2));
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+
+        assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mCheckBox.isChecked()).isTrue();
+        assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.GONE);
+
+        mViewHolder.mCheckBox.performClick();
+        verify(mMediaSwitchingController).removeDeviceFromPlayMedia(mMediaDevice2);
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING)
+    @Test
+    public void onBindViewHolder_changingSelectedValue_doesntTriggerChangeListener() {
+        List<MediaDevice> selectableDevices = List.of(mMediaDevice2);
+        List<MediaDevice> selectedDevices = new ArrayList<>();
+        selectedDevices.add(mMediaDevice1);
+        when(mMediaSwitchingController.getSelectableMediaDevice()).thenReturn(selectableDevices);
+        when(mMediaSwitchingController.getSelectedMediaDevice()).thenReturn(selectedDevices);
+
+        // mMediaDevice2 is selected
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+        assertThat(mViewHolder.mCheckBox.isChecked()).isFalse();
+
+        // changing the selected state programmatically (not a user click)
+        selectedDevices.add(mMediaDevice2);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+        assertThat(mViewHolder.mCheckBox.isChecked()).isTrue();
+
+        // The onCheckedChangeListener is not invoked
+        verify(mMediaSwitchingController, never()).addDeviceToPlayMedia(mMediaDevice2);
+        verify(mMediaSwitchingController, never()).removeDeviceFromPlayMedia(mMediaDevice2);
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 896ca26..3c8857f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -17,11 +17,14 @@
 package com.android.systemui.qs
 
 import android.content.res.Configuration
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.view.ContextThemeWrapper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.haptics.qs.QSLongPressEffect
@@ -33,6 +36,7 @@
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.util.leak.RotationUtils
 import javax.inject.Provider
@@ -63,6 +67,7 @@
     @Mock private lateinit var tile: QSTile
     @Mock private lateinit var tileLayout: TileLayout
     @Captor private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
+    @Captor private lateinit var configCaptor: ArgumentCaptor<ConfigurationListener>
     @Mock private lateinit var longPressEffectProvider: Provider<QSLongPressEffect>
     @Mock private lateinit var mediaCarouselInteractor: MediaCarouselInteractor
     @Mock private lateinit var configurationController: ConfigurationController
@@ -135,7 +140,8 @@
     }
 
     @Test
-    fun mediaExpansion_afterConfigChange_inLandscape_collapsedInLandscapeTrue_updatesToCollapsed() {
+    @DisableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+    fun mediaExpansion_afterConfigChange_inLandscape_collapsedInLandscapeTrue_updatesToCollapsed_old() {
         verify(quickQSPanel).addOnConfigurationChangedListener(captor.capture())
 
         // verify that media starts in the expanded state by default
@@ -150,7 +156,24 @@
     }
 
     @Test
-    fun mediaExpansion_afterConfigChange_landscape_collapsedInLandscapeFalse_remainsExpanded() {
+    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+    fun mediaExpansion_afterConfigChange_inLandscape_collapsedInLandscapeTrue_updatesToCollapsed() {
+        verify(configurationController).addCallback(configCaptor.capture())
+
+        // verify that media starts in the expanded state by default
+        verify(mediaHost).expansion = MediaHostState.EXPANDED
+
+        // Rotate device, verify media size updated to collapsed
+        usingCollapsedLandscapeMedia = true
+        controller.setRotation(RotationUtils.ROTATION_LANDSCAPE)
+        configCaptor.allValues.forEach { it.onConfigChanged(Configuration.EMPTY) }
+
+        verify(mediaHost).expansion = MediaHostState.COLLAPSED
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+    fun mediaExpansion_afterConfigChange_landscape_collapsedInLandscapeFalse_remainsExpanded_old() {
         verify(quickQSPanel).addOnConfigurationChangedListener(captor.capture())
         reset(mediaHost)
 
@@ -161,6 +184,19 @@
         verify(mediaHost).expansion = MediaHostState.EXPANDED
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+    fun mediaExpansion_afterConfigChange_landscape_collapsedInLandscapeFalse_remainsExpanded() {
+        verify(configurationController).addCallback(configCaptor.capture())
+        reset(mediaHost)
+
+        usingCollapsedLandscapeMedia = false
+        controller.setRotation(RotationUtils.ROTATION_LANDSCAPE)
+        configCaptor.allValues.forEach { it.onConfigChanged(Configuration.EMPTY) }
+
+        verify(mediaHost).expansion = MediaHostState.EXPANDED
+    }
+
     class TestQuickQSPanelController(
         view: QuickQSPanel,
         qsHost: QSHost,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index adaebbd..5527393 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -25,8 +25,10 @@
 import com.android.compose.animation.scene.DefaultEdgeDetector
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.fakeFalsingManager
-import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.power.data.repository.fakePowerRepository
@@ -44,17 +46,15 @@
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository
 import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -64,10 +64,6 @@
 
     private val kosmos = testKosmos()
     private val testScope by lazy { kosmos.testScope }
-    private val sceneInteractor by lazy { kosmos.sceneInteractor }
-    private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource }
-    private val sceneContainerConfig by lazy { kosmos.sceneContainerConfig }
-    private val fakeRemoteInputRepository by lazy { kosmos.fakeRemoteInputRepository }
     private val falsingManager by lazy { kosmos.fakeFalsingManager }
     private val view = mock<View>()
 
@@ -91,14 +87,14 @@
 
     @Test
     fun activate_setsMotionEventHandler() =
-        testScope.runTest {
+        kosmos.runTest {
             runCurrent()
             assertThat(motionEventHandler).isNotNull()
         }
 
     @Test
     fun deactivate_clearsMotionEventHandler() =
-        testScope.runTest {
+        kosmos.runTest {
             activationJob.cancel()
             runCurrent()
 
@@ -107,7 +103,7 @@
 
     @Test
     fun isVisible() =
-        testScope.runTest {
+        kosmos.runTest {
             assertThat(underTest.isVisible).isTrue()
 
             sceneInteractor.setVisible(false, "reason")
@@ -121,7 +117,7 @@
 
     @Test
     fun sceneTransition() =
-        testScope.runTest {
+        kosmos.runTest {
             val currentScene by collectLastValue(underTest.currentScene)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
 
@@ -132,7 +128,7 @@
 
     @Test
     fun canChangeScene_whenAllowed_switchingFromGone_returnsTrue() =
-        testScope.runTest {
+        kosmos.runTest {
             val currentScene by collectLastValue(underTest.currentScene)
             fakeSceneDataSource.changeScene(toScene = Scenes.Gone)
             runCurrent()
@@ -149,7 +145,7 @@
 
     @Test
     fun canChangeScene_whenAllowed_switchingFromLockscreen_returnsTrue() =
-        testScope.runTest {
+        kosmos.runTest {
             val currentScene by collectLastValue(underTest.currentScene)
             fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
             runCurrent()
@@ -166,7 +162,7 @@
 
     @Test
     fun canChangeScene_whenNotAllowed_fromLockscreen_toFalsingProtectedScenes_returnsFalse() =
-        testScope.runTest {
+        kosmos.runTest {
             falsingManager.setIsFalseTouch(true)
             val currentScene by collectLastValue(underTest.currentScene)
             fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
@@ -188,7 +184,7 @@
 
     @Test
     fun canChangeScene_whenNotAllowed_fromLockscreen_toFalsingUnprotectedScenes_returnsTrue() =
-        testScope.runTest {
+        kosmos.runTest {
             falsingManager.setIsFalseTouch(true)
             val currentScene by collectLastValue(underTest.currentScene)
             fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
@@ -209,7 +205,7 @@
 
     @Test
     fun canChangeScene_whenNotAllowed_fromGone_toAnyOtherScene_returnsTrue() =
-        testScope.runTest {
+        kosmos.runTest {
             falsingManager.setIsFalseTouch(true)
             val currentScene by collectLastValue(underTest.currentScene)
             fakeSceneDataSource.changeScene(toScene = Scenes.Gone)
@@ -226,8 +222,70 @@
         }
 
     @Test
+    fun canShowOrReplaceOverlay_whenAllowed_showingWhileOnGone_returnsTrue() =
+        kosmos.runTest {
+            val currentScene by collectLastValue(underTest.currentScene)
+            fakeSceneDataSource.changeScene(toScene = Scenes.Gone)
+            runCurrent()
+            assertThat(currentScene).isEqualTo(Scenes.Gone)
+
+            sceneContainerConfig.overlayKeys.forEach { overlay ->
+                assertWithMessage("Overlay $overlay incorrectly protected when allowed")
+                    .that(underTest.canShowOrReplaceOverlay(newlyShown = overlay))
+                    .isTrue()
+            }
+        }
+
+    @Test
+    fun canShowOrReplaceOverlay_whenAllowed_showingWhileOnLockscreen_returnsTrue() =
+        kosmos.runTest {
+            val currentScene by collectLastValue(underTest.currentScene)
+            fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
+            runCurrent()
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+
+            sceneContainerConfig.overlayKeys.forEach { overlay ->
+                assertWithMessage("Overlay $overlay incorrectly protected when allowed")
+                    .that(underTest.canShowOrReplaceOverlay(newlyShown = overlay))
+                    .isTrue()
+            }
+        }
+
+    @Test
+    fun canShowOrReplaceOverlay_whenNotAllowed_whileOnLockscreen_returnsFalse() =
+        kosmos.runTest {
+            falsingManager.setIsFalseTouch(true)
+            val currentScene by collectLastValue(underTest.currentScene)
+            fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
+            runCurrent()
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+
+            sceneContainerConfig.overlayKeys.forEach { overlay ->
+                assertWithMessage("Protected overlay $overlay not properly protected")
+                    .that(underTest.canShowOrReplaceOverlay(newlyShown = overlay))
+                    .isFalse()
+            }
+        }
+
+    @Test
+    fun canShowOrReplaceOverlay_whenNotAllowed_whileOnGone_returnsTrue() =
+        kosmos.runTest {
+            falsingManager.setIsFalseTouch(true)
+            val currentScene by collectLastValue(underTest.currentScene)
+            fakeSceneDataSource.changeScene(toScene = Scenes.Gone)
+            runCurrent()
+            assertThat(currentScene).isEqualTo(Scenes.Gone)
+
+            sceneContainerConfig.overlayKeys.forEach { overlay ->
+                assertWithMessage("Protected overlay $overlay not properly protected")
+                    .that(underTest.canShowOrReplaceOverlay(newlyShown = overlay))
+                    .isTrue()
+            }
+        }
+
+    @Test
     fun userInput() =
-        testScope.runTest {
+        kosmos.runTest {
             assertThat(kosmos.fakePowerRepository.userTouchRegistered).isFalse()
             underTest.onMotionEvent(mock())
             assertThat(kosmos.fakePowerRepository.userTouchRegistered).isTrue()
@@ -235,7 +293,7 @@
 
     @Test
     fun userInputOnEmptySpace_insideEvent() =
-        testScope.runTest {
+        kosmos.runTest {
             assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
             val insideMotionEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0f, 0f, 0)
             underTest.onEmptySpaceMotionEvent(insideMotionEvent)
@@ -244,7 +302,7 @@
 
     @Test
     fun userInputOnEmptySpace_outsideEvent_remoteInputActive() =
-        testScope.runTest {
+        kosmos.runTest {
             fakeRemoteInputRepository.isRemoteInputActive.value = true
             assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
             val outsideMotionEvent = MotionEvent.obtain(0, 0, ACTION_OUTSIDE, 0f, 0f, 0)
@@ -254,7 +312,7 @@
 
     @Test
     fun userInputOnEmptySpace_outsideEvent_remoteInputInactive() =
-        testScope.runTest {
+        kosmos.runTest {
             fakeRemoteInputRepository.isRemoteInputActive.value = false
             assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
             val outsideMotionEvent = MotionEvent.obtain(0, 0, ACTION_OUTSIDE, 0f, 0f, 0)
@@ -264,7 +322,7 @@
 
     @Test
     fun remoteUserInteraction_keepsContainerVisible() =
-        testScope.runTest {
+        kosmos.runTest {
             sceneInteractor.setVisible(false, "reason")
             runCurrent()
             assertThat(underTest.isVisible).isFalse()
@@ -272,9 +330,7 @@
             runCurrent()
             assertThat(underTest.isVisible).isTrue()
 
-            underTest.onMotionEvent(
-                mock { whenever(actionMasked).thenReturn(MotionEvent.ACTION_UP) }
-            )
+            underTest.onMotionEvent(mock { on { actionMasked } doReturn MotionEvent.ACTION_UP })
             runCurrent()
 
             assertThat(underTest.isVisible).isFalse()
@@ -282,7 +338,7 @@
 
     @Test
     fun getActionableContentKey_noOverlays_returnsCurrentScene() =
-        testScope.runTest {
+        kosmos.runTest {
             val currentScene by collectLastValue(underTest.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
@@ -300,7 +356,7 @@
 
     @Test
     fun getActionableContentKey_multipleOverlays_returnsTopOverlay() =
-        testScope.runTest {
+        kosmos.runTest {
             val currentScene by collectLastValue(underTest.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             fakeSceneDataSource.showOverlay(Overlays.QuickSettingsShade)
@@ -321,7 +377,7 @@
 
     @Test
     fun edgeDetector_singleShade_usesDefaultEdgeDetector() =
-        testScope.runTest {
+        kosmos.runTest {
             val shadeMode by collectLastValue(kosmos.shadeMode)
             kosmos.enableSingleShade()
 
@@ -331,7 +387,7 @@
 
     @Test
     fun edgeDetector_splitShade_usesDefaultEdgeDetector() =
-        testScope.runTest {
+        kosmos.runTest {
             val shadeMode by collectLastValue(kosmos.shadeMode)
             kosmos.enableSplitShade()
 
@@ -341,7 +397,7 @@
 
     @Test
     fun edgeDetector_dualShade_narrowScreen_usesSceneContainerSwipeDetector() =
-        testScope.runTest {
+        kosmos.runTest {
             val shadeMode by collectLastValue(kosmos.shadeMode)
             kosmos.enableDualShade(wideLayout = false)
 
@@ -352,7 +408,7 @@
 
     @Test
     fun edgeDetector_dualShade_wideScreen_usesSceneContainerSwipeDetector() =
-        testScope.runTest {
+        kosmos.runTest {
             val shadeMode by collectLastValue(kosmos.shadeMode)
             kosmos.enableDualShade(wideLayout = true)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/ConversationNotificationProcessorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/ConversationNotificationProcessorTest.kt
new file mode 100644
index 0000000..67d0ade
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/ConversationNotificationProcessorTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2025 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.statusbar.notification
+
+import android.app.Flags
+import android.app.Notification
+import android.app.Notification.EXTRA_SUMMARIZED_CONTENT
+import android.app.Person
+import android.content.pm.LauncherApps
+import android.content.pm.launcherApps
+import android.graphics.drawable.Icon
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper.RunWithLooper
+import android.text.SpannableStringBuilder
+import android.text.style.ImageSpan
+import android.text.style.StyleSpan
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.RankingBuilder
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinderLogger
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.android.systemui.statusbar.notification.row.notificationRowContentBinderLogger
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+class ConversationNotificationProcessorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private lateinit var conversationNotificationProcessor: ConversationNotificationProcessor
+    private lateinit var testHelper: NotificationTestHelper
+    private lateinit var launcherApps: LauncherApps
+    private lateinit var logger: NotificationRowContentBinderLogger
+    private lateinit var conversationNotificationManager: ConversationNotificationManager
+
+    @Before
+    fun setup() {
+        launcherApps = kosmos.launcherApps
+        conversationNotificationManager = kosmos.conversationNotificationManager
+        logger = kosmos.notificationRowContentBinderLogger
+        testHelper = NotificationTestHelper(mContext, mDependency)
+
+        conversationNotificationProcessor =
+            ConversationNotificationProcessor(
+                context,
+                launcherApps,
+                conversationNotificationManager,
+            )
+    }
+
+    @Test
+    fun processNotification_notMessagingStyle() {
+        val nb = Notification.Builder(mContext).setSmallIcon(R.drawable.ic_person)
+        val newRow: ExpandableNotificationRow = testHelper.createRow(nb.build())
+
+        assertThat(conversationNotificationProcessor.processNotification(newRow.entry, nb, logger))
+            .isNull()
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_NM_SUMMARIZATION, Flags.FLAG_NM_SUMMARIZATION_UI)
+    fun processNotification_messagingStyleWithSummarization_flagOff() {
+        val summarization = "hello"
+        val nb = getMessagingNotification()
+        val newRow: ExpandableNotificationRow = testHelper.createRow(nb.build())
+        newRow.entry.setRanking(
+            RankingBuilder(newRow.entry.ranking).setSummarization(summarization).build()
+        )
+
+        assertThat(conversationNotificationProcessor.processNotification(newRow.entry, nb, logger))
+            .isNotNull()
+        assertThat(nb.build().extras.getCharSequence(EXTRA_SUMMARIZED_CONTENT)).isNull()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NM_SUMMARIZATION)
+    fun processNotification_messagingStyleWithSummarization() {
+        val summarization = "hello"
+        val nb = getMessagingNotification()
+        val newRow: ExpandableNotificationRow = testHelper.createRow(nb.build())
+        newRow.entry.setRanking(
+            RankingBuilder(newRow.entry.ranking).setSummarization(summarization).build()
+        )
+
+        assertThat(conversationNotificationProcessor.processNotification(newRow.entry, nb, logger))
+            .isNotNull()
+
+        val processedSummary = nb.build().extras.getCharSequence(EXTRA_SUMMARIZED_CONTENT)
+        assertThat(processedSummary.toString()).isEqualTo("x$summarization")
+
+        val checkSpans = SpannableStringBuilder(processedSummary)
+        assertThat(
+                checkSpans.getSpans(
+                    /* queryStart = */ 0,
+                    /* queryEnd = */ 1,
+                    /* kind = */ ImageSpan::class.java,
+                )
+            )
+            .isNotNull()
+
+        assertThat(
+                processedSummary?.let {
+                    checkSpans.getSpans(
+                        /* queryStart = */ 0,
+                        /* queryEnd = */ it.length,
+                        /* kind = */ StyleSpan::class.java,
+                    )
+                }
+            )
+            .isNotNull()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NM_SUMMARIZATION)
+    fun processNotification_messagingStyleWithoutSummarization() {
+        val nb = getMessagingNotification()
+        val newRow: ExpandableNotificationRow = testHelper.createRow(nb.build())
+        assertThat(conversationNotificationProcessor.processNotification(newRow.entry, nb, logger))
+            .isNotNull()
+        assertThat(nb.build().extras.getCharSequence(EXTRA_SUMMARIZED_CONTENT)).isNull()
+    }
+
+    private fun getMessagingNotification(): Notification.Builder {
+        val displayName = "Display Name"
+        val messageText = "Message Text"
+        val personIcon = Icon.createWithResource(mContext, R.drawable.ic_person)
+        val testPerson = Person.Builder().setName(displayName).setIcon(personIcon).build()
+        val messagingStyle = Notification.MessagingStyle(testPerson)
+        messagingStyle.addMessage(
+            Notification.MessagingStyle.Message(messageText, System.currentTimeMillis(), testPerson)
+        )
+        return Notification.Builder(mContext)
+            .setSmallIcon(R.drawable.ic_person)
+            .setStyle(messagingStyle)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index da31cd9..7d406b4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -209,6 +209,26 @@
     }
 
     @Test
+    public void testInflationProcessesMessagingStyle() throws Exception {
+        String displayName = "Display Name";
+        String messageText = "Message Text";
+        Icon personIcon = Icon.createWithResource(
+                mContext, com.android.systemui.res.R.drawable.ic_person);
+        Person testPerson = new Person.Builder().setName(displayName).setIcon(personIcon).build();
+        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle(testPerson);
+        messagingStyle.addMessage(new Notification.MessagingStyle.Message(
+                messageText, System.currentTimeMillis(), testPerson));
+        Notification messageNotif = new Notification.Builder(mContext)
+                .setSmallIcon(com.android.systemui.res.R.drawable.ic_person)
+                .setStyle(messagingStyle)
+                .build();
+        ExpandableNotificationRow newRow = mHelper.createRow(messageNotif);
+        inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, newRow);
+
+        verify(mConversationNotificationProcessor).processNotification(any(), any(), any());
+    }
+
+    @Test
     @Ignore
     public void testInflationIsRetriedIfAsyncFails() throws Exception {
         NotificationContentInflater.InflationProgress result =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index c77b09a..82eca37 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -587,6 +587,35 @@
         Assert.assertFalse(hasText(publicView, contentTitle))
     }
 
+    @Test
+    @Throws(java.lang.Exception::class)
+    @EnableFlags(android.app.Flags.FLAG_NM_SUMMARIZATION)
+    fun testAllMessagingStyleProcessedAsConversations() {
+        val displayName = "Display Name"
+        val messageText = "Message Text"
+        val personIcon = Icon.createWithResource(mContext, R.drawable.ic_person)
+        val testPerson = Person.Builder().setName(displayName).setIcon(personIcon).build()
+        val messagingStyle = Notification.MessagingStyle(testPerson)
+        messagingStyle.addMessage(
+            Notification.MessagingStyle.Message(messageText, System.currentTimeMillis(), testPerson)
+        )
+        val messageNotif =
+            Notification.Builder(mContext)
+                .setSmallIcon(R.drawable.ic_person)
+                .setStyle(messagingStyle)
+                .build()
+        val newRow: ExpandableNotificationRow = testHelper.createRow(messageNotif)
+
+        inflateAndWait(
+            false,
+            notificationInflater,
+            FLAG_CONTENT_VIEW_ALL,
+            REDACTION_TYPE_NONE,
+            newRow,
+        )
+        verify(conversationNotificationProcessor).processNotification(any(), any(), any())
+    }
+
     private class ExceptionHolder {
         var exception: Exception? = null
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index fc589b2..e36e855 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -19,11 +19,8 @@
 import android.os.UserHandle
 import android.provider.Settings
 import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.TransitionKey
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.CoreStartable
-import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -31,36 +28,24 @@
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalScenes.isCommunal
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
-import com.android.systemui.communal.shared.model.EditModeState
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dock.DockManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.statusbar.NotificationShadeWindowController
-import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.util.kotlin.emitOnStart
-import com.android.systemui.util.kotlin.getValue
 import com.android.systemui.util.kotlin.sample
 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import com.android.systemui.util.settings.SystemSettings
-import java.util.Optional
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.withContext
 
@@ -72,16 +57,12 @@
 class CommunalSceneStartable
 @Inject
 constructor(
-    private val dockManager: DockManager,
     private val communalInteractor: CommunalInteractor,
     private val communalSettingsInteractor: CommunalSettingsInteractor,
     private val communalSceneInteractor: CommunalSceneInteractor,
-    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val keyguardInteractor: KeyguardInteractor,
     private val systemSettings: SystemSettings,
-    centralSurfacesOpt: Optional<CentralSurfaces>,
     private val notificationShadeWindowController: NotificationShadeWindowController,
-    @Application private val applicationScope: CoroutineScope,
     @Background private val bgScope: CoroutineScope,
     @Main private val mainDispatcher: CoroutineDispatcher,
     private val uiEventLogger: UiEventLogger,
@@ -92,110 +73,11 @@
 
     private var isDreaming: Boolean = false
 
-    private val centralSurfaces: CentralSurfaces? by centralSurfacesOpt
-
     override fun start() {
         if (!communalSettingsInteractor.isCommunalFlagEnabled()) {
             return
         }
 
-        if (!communalSceneKtfRefactor()) {
-            // Handle automatically switching based on keyguard state.
-            keyguardTransitionInteractor.startedKeyguardTransitionStep
-                .mapLatest(::determineSceneAfterTransition)
-                .filterNotNull()
-                .onEach { (nextScene, nextTransition) ->
-                    // When launching a widget, we don't want to animate the scene change or the
-                    // Communal Hub will reveal the wallpaper even though it shouldn't. Instead we
-                    // snap to the new scene as part of the launch animation, once the activity
-                    // launch is done, so we don't change scene here.
-                    val delaySceneTransition =
-                        communalSceneInteractor.editModeState.value == EditModeState.STARTING ||
-                            communalSceneInteractor.isLaunchingWidget.value
-                    if (!delaySceneTransition) {
-                        communalSceneInteractor.changeScene(
-                            newScene = nextScene,
-                            loggingReason = "KTF syncing",
-                            transitionKey = nextTransition,
-                        )
-                    }
-                }
-                .launchIn(applicationScope)
-        }
-
-        // TODO(b/322787129): re-enable once custom animations are in place
-        // Handle automatically switching to communal when docked.
-        //        dockManager
-        //            .retrieveIsDocked()
-        //            // Allow some time after docking to ensure the dream doesn't start. If the
-        // dream
-        //            // starts, then we don't want to automatically transition to glanceable hub.
-        //            .debounce(DOCK_DEBOUNCE_DELAY)
-        //            .sample(keyguardTransitionInteractor.startedKeyguardState, ::Pair)
-        //            .onEach { (docked, lastStartedState) ->
-        //                if (docked && lastStartedState == KeyguardState.LOCKSCREEN) {
-        //                    communalInteractor.onSceneChanged(CommunalScenes.Communal)
-        //                }
-        //            }
-        //            .launchIn(bgScope)
-
-        systemSettings
-            .observerFlow(Settings.System.SCREEN_OFF_TIMEOUT)
-            // Read the setting value on start.
-            .emitOnStart()
-            .onEach {
-                screenTimeout =
-                    systemSettings.getIntForUser(
-                        Settings.System.SCREEN_OFF_TIMEOUT,
-                        DEFAULT_SCREEN_TIMEOUT,
-                        UserHandle.USER_CURRENT,
-                    )
-            }
-            .launchIn(bgScope)
-
-        // The hub mode timeout should start as soon as the user enters hub mode. At the end of the
-        // timer, if the device is dreaming, hub mode should closed and reveal the dream. If the
-        // dream is not running, nothing will happen. However if the dream starts again underneath
-        // hub mode after the initial timeout expires, such as if the device is docked or the dream
-        // app is updated by the Play store, a new timeout should be started.
-        bgScope.launch {
-            combine(
-                    communalSceneInteractor.currentScene,
-                    // Emit a value on start so the combine starts.
-                    communalInteractor.userActivity.emitOnStart(),
-                ) { scene, _ ->
-                    // Only timeout if we're on the hub is open.
-                    scene.isCommunal()
-                }
-                .collectLatest { shouldTimeout ->
-                    cancelHubTimeout()
-                    if (shouldTimeout) {
-                        startHubTimeout()
-                    }
-                }
-        }
-        bgScope.launch {
-            keyguardInteractor.isDreaming
-                .sample(communalSceneInteractor.currentScene, ::Pair)
-                .collectLatest { (isDreaming, scene) ->
-                    this@CommunalSceneStartable.isDreaming = isDreaming
-                    if (scene.isCommunal() && isDreaming && timeoutJob == null) {
-                        // If dreaming starts after timeout has expired, ex. if dream restarts under
-                        // the hub, wait for IS_ABLE_TO_DREAM_DELAY_MS and then close the hub. The
-                        // delay is necessary so the KeyguardInteractor.isAbleToDream flow passes
-                        // through that same amount of delay and publishes a new value which is then
-                        // picked up by the HomeSceneFamilyResolver such that the next call to
-                        // SceneInteractor.changeScene(Home) will resolve "Home" to "Dream".
-                        delay(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
-                        communalSceneInteractor.changeScene(
-                            CommunalScenes.Blank,
-                            "dream started after timeout",
-                        )
-                        uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT)
-                    }
-                }
-        }
-
         bgScope.launch {
             communalSceneInteractor.isIdleOnCommunal.collectLatest {
                 withContext(mainDispatcher) {
@@ -203,6 +85,75 @@
                 }
             }
         }
+
+        // In V2, the timeout is handled by PowerManagerService since we no longer keep the dream
+        // active underneath the hub.
+        if (!communalSettingsInteractor.isV2FlagEnabled()) {
+            systemSettings
+                .observerFlow(Settings.System.SCREEN_OFF_TIMEOUT)
+                // Read the setting value on start.
+                .emitOnStart()
+                .onEach {
+                    screenTimeout =
+                        systemSettings.getIntForUser(
+                            Settings.System.SCREEN_OFF_TIMEOUT,
+                            DEFAULT_SCREEN_TIMEOUT,
+                            UserHandle.USER_CURRENT,
+                        )
+                }
+                .launchIn(bgScope)
+
+            // The hub mode timeout should start as soon as the user enters hub mode. At the end of
+            // the
+            // timer, if the device is dreaming, hub mode should closed and reveal the dream. If the
+            // dream is not running, nothing will happen. However if the dream starts again
+            // underneath
+            // hub mode after the initial timeout expires, such as if the device is docked or the
+            // dream
+            // app is updated by the Play store, a new timeout should be started.
+            bgScope.launch {
+                combine(
+                        communalSceneInteractor.currentScene,
+                        // Emit a value on start so the combine starts.
+                        communalInteractor.userActivity.emitOnStart(),
+                    ) { scene, _ ->
+                        // Only timeout if we're on the hub is open.
+                        scene.isCommunal()
+                    }
+                    .collectLatest { shouldTimeout ->
+                        cancelHubTimeout()
+                        if (shouldTimeout) {
+                            startHubTimeout()
+                        }
+                    }
+            }
+
+            bgScope.launch {
+                keyguardInteractor.isDreaming
+                    .sample(communalSceneInteractor.currentScene, ::Pair)
+                    .collectLatest { (isDreaming, scene) ->
+                        this@CommunalSceneStartable.isDreaming = isDreaming
+                        if (scene.isCommunal() && isDreaming && timeoutJob == null) {
+                            // If dreaming starts after timeout has expired, ex. if dream restarts
+                            // under
+                            // the hub, wait for IS_ABLE_TO_DREAM_DELAY_MS and then close the hub.
+                            // The
+                            // delay is necessary so the KeyguardInteractor.isAbleToDream flow
+                            // passes
+                            // through that same amount of delay and publishes a new value which is
+                            // then
+                            // picked up by the HomeSceneFamilyResolver such that the next call to
+                            // SceneInteractor.changeScene(Home) will resolve "Home" to "Dream".
+                            delay(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
+                            communalSceneInteractor.changeScene(
+                                CommunalScenes.Blank,
+                                "dream started after timeout",
+                            )
+                            uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT)
+                        }
+                    }
+            }
+        }
     }
 
     private fun cancelHubTimeout() {
@@ -231,49 +182,7 @@
         }
     }
 
-    private suspend fun determineSceneAfterTransition(
-        lastStartedTransition: TransitionStep
-    ): Pair<SceneKey, TransitionKey>? {
-        val to = lastStartedTransition.to
-        val from = lastStartedTransition.from
-        val docked = dockManager.isDocked
-        val launchingActivityOverLockscreen =
-            centralSurfaces?.isLaunchingActivityOverLockscreen ?: false
-
-        return when {
-            to == KeyguardState.OCCLUDED && !launchingActivityOverLockscreen -> {
-                // Hide communal when an activity is started on keyguard, to ensure the activity
-                // underneath the hub is shown. When launching activities over lockscreen, we only
-                // change scenes once the activity launch animation is finished, so avoid
-                // changing the scene here.
-                Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
-            }
-            to == KeyguardState.GLANCEABLE_HUB && from == KeyguardState.OCCLUDED -> {
-                // When transitioning to the hub from an occluded state, fade out the hub without
-                // doing any translation.
-                Pair(CommunalScenes.Communal, CommunalTransitionKeys.SimpleFade)
-            }
-            // Transitioning to Blank scene when entering the edit mode will be handled separately
-            // with custom animations.
-            to == KeyguardState.GONE && !communalInteractor.editModeOpen.value ->
-                Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
-            !docked && !KeyguardState.deviceIsAwakeInState(to) -> {
-                // If the user taps the screen and wakes the device within this timeout, we don't
-                // want to dismiss the hub
-                delay(AWAKE_DEBOUNCE_DELAY)
-                Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
-            }
-            from == KeyguardState.DOZING && to == KeyguardState.GLANCEABLE_HUB -> {
-                // Make sure the communal hub is showing when transitioning from dozing to hub.
-                Pair(CommunalScenes.Communal, CommunalTransitionKeys.SimpleFade)
-            }
-            else -> null
-        }
-    }
-
     companion object {
-        val AWAKE_DEBOUNCE_DELAY = 5.seconds
-        val DOCK_DEBOUNCE_DELAY = 1.seconds
         val DEFAULT_SCREEN_TIMEOUT = 15000
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
index ec55401..740555f4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
@@ -16,11 +16,11 @@
 
 package com.android.systemui.communal.domain.interactor
 
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayout
 import com.android.systemui.CoreStartable
-import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.data.repository.CommunalSceneTransitionRepository
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.dagger.SysUISingleton
@@ -44,7 +44,6 @@
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /**
  * This class listens to [SceneTransitionLayout] transitions and manages keyguard transition
@@ -122,11 +121,7 @@
             )
 
     override fun start() {
-        if (
-            communalSceneKtfRefactor() &&
-                settingsInteractor.isCommunalFlagEnabled() &&
-                !SceneContainerFlag.isEnabled
-        ) {
+        if (settingsInteractor.isCommunalFlagEnabled() && !SceneContainerFlag.isEnabled) {
             sceneInteractor.registerSceneStateProcessor(this)
             listenForSceneTransitionProgress()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index a4860df..d061a33 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -153,7 +153,7 @@
     ) {}
 
     /** Called as the UI requests deleting a widget. */
-    open fun onDeleteWidget(id: Int, componentName: ComponentName, rank: Int) {}
+    open fun onDeleteWidget(id: Int, key: String, componentName: ComponentName, rank: Int) {}
 
     /** Called as the UI detects a tap event on the widget. */
     open fun onTapWidget(componentName: ComponentName, rank: Int) {}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 8aba111..59beb1e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -141,7 +141,10 @@
         metricsLogger.logAddWidget(componentName.flattenToString(), rank)
     }
 
-    override fun onDeleteWidget(id: Int, componentName: ComponentName, rank: Int) {
+    override fun onDeleteWidget(id: Int, key: String, componentName: ComponentName, rank: Int) {
+        if (selectedKey.value == key) {
+            setSelectedKey(null)
+        }
         communalInteractor.deleteWidget(id)
         metricsLogger.logRemoveWidget(componentName.flattenToString(), rank)
     }
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 eb96c92..6f5f662 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
@@ -21,7 +21,6 @@
 import android.app.DreamManager
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -155,9 +154,8 @@
                 .sample(
                     communalInteractor.isCommunalAvailable,
                     communalInteractor.shouldShowCommunal,
-                    communalSceneInteractor.isIdleOnCommunal,
                 )
-                .collect { (_, isCommunalAvailable, shouldShowCommunal, isIdleOnCommunal) ->
+                .collect { (_, isCommunalAvailable, shouldShowCommunal) ->
                     val isKeyguardOccludedLegacy = keyguardInteractor.isKeyguardOccluded.value
                     val primaryBouncerShowing = keyguardInteractor.primaryBouncerShowing.value
                     val isKeyguardGoingAway = keyguardInteractor.isKeyguardGoingAway.value
@@ -187,10 +185,6 @@
                         }
                     } else if (isKeyguardOccludedLegacy) {
                         startTransitionTo(KeyguardState.OCCLUDED)
-                    } else if (isIdleOnCommunal && !communalSceneKtfRefactor()) {
-                        if (!SceneContainerFlag.isEnabled) {
-                            startTransitionTo(KeyguardState.GLANCEABLE_HUB)
-                        }
                     } else if (
                         shouldTransitionToCommunal(shouldShowCommunal, isCommunalAvailable)
                     ) {
@@ -217,7 +211,6 @@
                 .sample(
                     communalInteractor.shouldShowCommunal,
                     communalInteractor.isCommunalAvailable,
-                    communalSceneInteractor.isIdleOnCommunal,
                     keyguardInteractor.biometricUnlockState,
                     wakeToGoneInteractor.canWakeDirectlyToGone,
                     keyguardInteractor.primaryBouncerShowing,
@@ -227,7 +220,6 @@
                         _,
                         shouldShowCommunal,
                         isCommunalAvailable,
-                        isIdleOnCommunal,
                         biometricUnlockState,
                         canWakeDirectlyToGone,
                         primaryBouncerShowing) ->
@@ -252,13 +244,6 @@
                                     ownerReason = "waking from dozing",
                                 )
                             }
-                        } else if (isIdleOnCommunal && !communalSceneKtfRefactor()) {
-                            if (!SceneContainerFlag.isEnabled) {
-                                startTransitionTo(
-                                    KeyguardState.GLANCEABLE_HUB,
-                                    ownerReason = "waking from dozing",
-                                )
-                            }
                         } else if (
                             shouldTransitionToCommunal(shouldShowCommunal, isCommunalAvailable)
                         ) {
@@ -276,15 +261,11 @@
         }
     }
 
-    private suspend fun transitionToGlanceableHub() {
-        if (communalSceneKtfRefactor()) {
-            communalSceneInteractor.snapToScene(
-                newScene = CommunalScenes.Communal,
-                loggingReason = "from dozing to hub",
-            )
-        } else {
-            startTransitionTo(KeyguardState.GLANCEABLE_HUB)
-        }
+    private fun transitionToGlanceableHub() {
+        communalSceneInteractor.snapToScene(
+            newScene = CommunalScenes.Communal,
+            loggingReason = "from dozing to hub",
+        )
     }
 
     /** Dismisses keyguard from the DOZING state. */
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 c1c509b..0fb98ff 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
@@ -21,7 +21,6 @@
 import android.app.DreamManager
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -59,7 +58,6 @@
     @Background bgDispatcher: CoroutineDispatcher,
     @Main mainDispatcher: CoroutineDispatcher,
     keyguardInteractor: KeyguardInteractor,
-    private val glanceableHubTransitions: GlanceableHubTransitions,
     private val communalInteractor: CommunalInteractor,
     private val communalSceneInteractor: CommunalSceneInteractor,
     private val communalSettingsInteractor: CommunalSettingsInteractor,
@@ -87,11 +85,7 @@
         listenForDreamingToLockscreenOrGone()
         listenForDreamingToAodOrDozing()
         listenForTransitionToCamera(scope, keyguardInteractor)
-        if (!communalSceneKtfRefactor()) {
-            listenForDreamingToGlanceableHub()
-        } else {
-            listenForDreamingToGlanceableHubFromPowerButton()
-        }
+        listenForDreamingToGlanceableHubFromPowerButton()
         listenForDreamingToPrimaryBouncer()
     }
 
@@ -105,18 +99,6 @@
         }
     }
 
-    private fun listenForDreamingToGlanceableHub() {
-        if (!communalSettingsInteractor.isCommunalFlagEnabled()) return
-        if (SceneContainerFlag.isEnabled) return
-        scope.launch("$TAG#listenForDreamingToGlanceableHub", mainDispatcher) {
-            glanceableHubTransitions.listenForGlanceableHubTransition(
-                transitionOwnerName = TAG,
-                fromState = KeyguardState.DREAMING,
-                toState = KeyguardState.GLANCEABLE_HUB,
-            )
-        }
-    }
-
     /**
      * Normally when pressing power button from the dream, the devices goes from DREAMING to DOZING,
      * then [FromDozingTransitionInteractor] handles the transition to GLANCEABLE_HUB. However if
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index c5d40a0..2eeba0f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -19,7 +19,6 @@
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -31,12 +30,10 @@
 import com.android.systemui.keyguard.KeyguardWmStateRefactor
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.noneOf
-import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
@@ -45,10 +42,8 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.FlowPreview
-import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.debounce
-import kotlinx.coroutines.withContext
 
 @OptIn(FlowPreview::class)
 @SysUISingleton
@@ -58,7 +53,6 @@
     @Background private val scope: CoroutineScope,
     @Main mainDispatcher: CoroutineDispatcher,
     @Background bgDispatcher: CoroutineDispatcher,
-    private val glanceableHubTransitions: GlanceableHubTransitions,
     private val communalSettingsInteractor: CommunalSettingsInteractor,
     keyguardInteractor: KeyguardInteractor,
     private val communalSceneInteractor: CommunalSceneInteractor,
@@ -83,9 +77,6 @@
         if (!communalSettingsInteractor.isCommunalFlagEnabled()) {
             return
         }
-        if (!communalSceneKtfRefactor()) {
-            listenForHubToLockscreenOrDreaming()
-        }
         listenForHubToDozing()
         listenForHubToPrimaryBouncer()
         listenForHubToAlternateBouncer()
@@ -108,30 +99,6 @@
         }
     }
 
-    /**
-     * Listens for the glanceable hub transition to lock screen and directly drives the keyguard
-     * transition.
-     */
-    private fun listenForHubToLockscreenOrDreaming() {
-        scope.launch("$TAG#listenForGlanceableHubToLockscreenOrDream") {
-            keyguardInteractor.isDreaming.collectLatest { dreaming ->
-                withContext(mainDispatcher) {
-                    val toState =
-                        if (dreaming) {
-                            KeyguardState.DREAMING
-                        } else {
-                            KeyguardState.LOCKSCREEN
-                        }
-                    glanceableHubTransitions.listenForGlanceableHubTransition(
-                        transitionOwnerName = TAG,
-                        fromState = KeyguardState.GLANCEABLE_HUB,
-                        toState = toState,
-                    )
-                }
-            }
-        }
-    }
-
     private fun listenForHubToPrimaryBouncer() {
         scope.launch("$TAG#listenForHubToPrimaryBouncer") {
             keyguardInteractor.primaryBouncerShowing
@@ -161,18 +128,11 @@
             powerInteractor.isAsleep
                 .filterRelevantKeyguardStateAnd { isAsleep -> isAsleep }
                 .collect {
-                    if (communalSceneKtfRefactor()) {
-                        communalSceneInteractor.snapToScene(
-                            newScene = CommunalScenes.Blank,
-                            loggingReason = "hub to dozing",
-                            keyguardState = KeyguardState.DOZING,
-                        )
-                    } else {
-                        startTransitionTo(
-                            toState = KeyguardState.DOZING,
-                            modeOnCanceled = TransitionModeOnCanceled.LAST_VALUE,
-                        )
-                    }
+                    communalSceneInteractor.snapToScene(
+                        newScene = CommunalScenes.Blank,
+                        loggingReason = "hub to dozing",
+                        keyguardState = KeyguardState.DOZING,
+                    )
                 }
         }
     }
@@ -202,21 +162,17 @@
                     .filterRelevantKeyguardStateAnd { onTop -> onTop }
                     .collect {
                         maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
-                            if (communalSceneKtfRefactor()) {
-                                communalSceneInteractor.changeScene(
-                                    newScene = CommunalScenes.Blank,
-                                    loggingReason = "hub to occluded (KeyguardWmStateRefactor)",
-                                    transitionKey = CommunalTransitionKeys.SimpleFade,
-                                    keyguardState = state,
-                                )
-                                null
-                            } else {
-                                startTransitionTo(state, ownerReason = reason)
-                            }
+                            communalSceneInteractor.changeScene(
+                                newScene = CommunalScenes.Blank,
+                                loggingReason = "hub to occluded (KeyguardWmStateRefactor)",
+                                transitionKey = CommunalTransitionKeys.SimpleFade,
+                                keyguardState = state,
+                            )
+                            null
                         }
                     }
             }
-        } else if (communalSceneKtfRefactor()) {
+        } else {
             scope.launch {
                 combine(
                         keyguardInteractor.isKeyguardOccluded,
@@ -248,56 +204,40 @@
                         )
                     }
             }
-        } else {
-            scope.launch {
-                allOf(keyguardInteractor.isKeyguardOccluded, not(keyguardInteractor.isDreaming))
-                    .filterRelevantKeyguardStateAnd { isOccludedAndNotDreaming ->
-                        isOccludedAndNotDreaming
-                    }
-                    .collect { _ -> startTransitionTo(KeyguardState.OCCLUDED) }
-            }
         }
     }
 
     private fun listenForHubToGone() {
         if (SceneContainerFlag.isEnabled) return
-        if (communalSceneKtfRefactor()) {
-            scope.launch {
-                allOf(
-                        keyguardInteractor.isKeyguardGoingAway,
-                        // TODO(b/327225415): Handle edit mode opening here to avoid going to GONE
-                        // state until after edit mode is ready to be shown.
-                        noneOf(
-                            // When launching activities from widgets on the hub, we wait to change
-                            // scenes until the activity launch is complete.
-                            communalSceneInteractor.isLaunchingWidget
-                        ),
-                    )
-                    .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
-                    .sample(communalSceneInteractor.editModeState, ::Pair)
-                    .collect { (_, editModeState) ->
-                        if (
-                            editModeState == EditModeState.STARTING ||
-                                editModeState == EditModeState.SHOWING
-                        ) {
-                            // Don't change scenes here as that is handled by the edit activity.
-                            startTransitionTo(KeyguardState.GONE)
-                        } else {
-                            communalSceneInteractor.changeScene(
-                                newScene = CommunalScenes.Blank,
-                                loggingReason = "hub to gone",
-                                transitionKey = CommunalTransitionKeys.SimpleFade,
-                                keyguardState = KeyguardState.GONE,
-                            )
-                        }
+        scope.launch {
+            allOf(
+                    keyguardInteractor.isKeyguardGoingAway,
+                    // TODO(b/327225415): Handle edit mode opening here to avoid going to GONE
+                    // state until after edit mode is ready to be shown.
+                    noneOf(
+                        // When launching activities from widgets on the hub, we wait to change
+                        // scenes until the activity launch is complete.
+                        communalSceneInteractor.isLaunchingWidget
+                    ),
+                )
+                .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
+                .sample(communalSceneInteractor.editModeState, ::Pair)
+                .collect { (_, editModeState) ->
+                    if (
+                        editModeState == EditModeState.STARTING ||
+                            editModeState == EditModeState.SHOWING
+                    ) {
+                        // Don't change scenes here as that is handled by the edit activity.
+                        startTransitionTo(KeyguardState.GONE)
+                    } else {
+                        communalSceneInteractor.changeScene(
+                            newScene = CommunalScenes.Blank,
+                            loggingReason = "hub to gone",
+                            transitionKey = CommunalTransitionKeys.SimpleFade,
+                            keyguardState = KeyguardState.GONE,
+                        )
                     }
-            }
-        } else {
-            scope.launch {
-                keyguardInteractor.isKeyguardGoingAway
-                    .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
-                    .collect { startTransitionTo(KeyguardState.GONE) }
-            }
+                }
         }
     }
 
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 cf712f1..a01dc02 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
@@ -20,7 +20,6 @@
 import android.util.MathUtils
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -69,7 +68,6 @@
     keyguardInteractor: KeyguardInteractor,
     private val shadeRepository: ShadeRepository,
     powerInteractor: PowerInteractor,
-    private val glanceableHubTransitions: GlanceableHubTransitions,
     private val communalSettingsInteractor: CommunalSettingsInteractor,
     private val communalInteractor: CommunalInteractor,
     private val communalSceneInteractor: CommunalSceneInteractor,
@@ -96,9 +94,6 @@
         listenForLockscreenToPrimaryBouncerDragging()
         listenForLockscreenToAlternateBouncer()
         listenForLockscreenTransitionToCamera()
-        if (!communalSceneKtfRefactor()) {
-            listenForLockscreenToGlanceableHub()
-        }
         if (communalSettingsInteractor.isV2FlagEnabled()) {
             listenForLockscreenToGlanceableHubV2()
         }
@@ -358,24 +353,6 @@
         }
     }
 
-    /**
-     * Listens for transition from glanceable hub back to lock screen and directly drives the
-     * keyguard transition.
-     */
-    private fun listenForLockscreenToGlanceableHub() {
-        if (SceneContainerFlag.isEnabled) return
-        if (!communalSettingsInteractor.isCommunalFlagEnabled()) {
-            return
-        }
-        scope.launch(context = mainDispatcher) {
-            glanceableHubTransitions.listenForGlanceableHubTransition(
-                transitionOwnerName = TAG,
-                fromState = KeyguardState.LOCKSCREEN,
-                toState = KeyguardState.GLANCEABLE_HUB,
-            )
-        }
-    }
-
     private fun listenForLockscreenToGlanceableHubV2() {
         scope.launch {
             communalInteractor.shouldShowCommunal
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index 284298d..a2cfdc1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -19,7 +19,6 @@
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.Flags.restartDreamOnUnocclude
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -143,15 +142,11 @@
             startTransitionTo(KeyguardState.DREAMING)
         } else if (isIdleOnCommunal || showCommunalFromOccluded) {
             if (SceneContainerFlag.isEnabled) return
-            if (communalSceneKtfRefactor()) {
-                communalSceneInteractor.changeScene(
-                    newScene = CommunalScenes.Communal,
-                    loggingReason = "occluded to hub",
-                    transitionKey = CommunalTransitionKeys.SimpleFade,
-                )
-            } else {
-                startTransitionTo(KeyguardState.GLANCEABLE_HUB)
-            }
+            communalSceneInteractor.changeScene(
+                newScene = CommunalScenes.Communal,
+                loggingReason = "occluded to hub",
+                transitionKey = CommunalTransitionKeys.SimpleFade,
+            )
         } else {
             startTransitionTo(KeyguardState.LOCKSCREEN)
         }
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 402c152..30c1aac 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
@@ -18,8 +18,8 @@
 
 import android.animation.ValueAnimator
 import android.util.Log
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.keyguard.KeyguardSecurityModel
-import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.dagger.SysUISingleton
@@ -35,7 +35,6 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.kotlin.Utils.Companion.sample
-import com.android.systemui.util.kotlin.sample
 import com.android.wm.shell.shared.animation.Interpolators
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -45,7 +44,6 @@
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 @SysUISingleton
 class FromPrimaryBouncerTransitionInteractor
@@ -173,8 +171,7 @@
         // If the hub is showing, and we are not animating a widget launch nor transitioning to
         // edit mode, then close the hub immediately.
         if (
-            communalSceneKtfRefactor() &&
-                communalSceneInteractor.isIdleOnCommunal.value &&
+            communalSceneInteractor.isIdleOnCommunal.value &&
                 !communalSceneInteractor.isLaunchingWidget.value &&
                 communalSceneInteractor.editModeState.value == null
         ) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
deleted file mode 100644
index bde0f56..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.domain.interactor
-
-import android.animation.ValueAnimator
-import com.android.app.animation.Interpolators
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
-import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionInfo
-import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.util.kotlin.sample
-import java.util.UUID
-import javax.inject.Inject
-
-class GlanceableHubTransitions
-@Inject
-constructor(
-    private val transitionInteractor: KeyguardTransitionInteractor,
-    private val transitionRepository: KeyguardTransitionRepository,
-    private val communalInteractor: CommunalInteractor,
-) {
-    /**
-     * Listens for the glanceable hub transition to the specified scene and directly drives the
-     * keyguard transition between the lockscreen and the hub.
-     *
-     * The glanceable hub transition progress is used as the source of truth as it cannot be driven
-     * externally. The progress is used for both transitions caused by user touch input or by
-     * programmatic changes.
-     */
-    suspend fun listenForGlanceableHubTransition(
-        transitionOwnerName: String,
-        fromState: KeyguardState,
-        toState: KeyguardState,
-    ) {
-        if (SceneContainerFlag.isEnabled) return
-        val toScene =
-            if (fromState == KeyguardState.GLANCEABLE_HUB) {
-                CommunalScenes.Blank
-            } else {
-                CommunalScenes.Communal
-            }
-        var transitionId: UUID? = null
-
-        communalInteractor
-            .transitionProgressToScene(toScene)
-            .sample(
-                transitionInteractor.startedKeyguardTransitionStep,
-                ::Pair,
-            )
-            .collect { (transitionProgress, lastStartedStep) ->
-                val id = transitionId
-                if (id == null) {
-                    // No transition started.
-                    if (
-                        transitionProgress is CommunalTransitionProgressModel.Transition &&
-                            lastStartedStep.to == fromState
-                    ) {
-                        transitionId =
-                            transitionRepository.startTransition(
-                                TransitionInfo(
-                                    ownerName = transitionOwnerName,
-                                    from = fromState,
-                                    to = toState,
-                                    animator = null, // transition will be manually controlled
-                                )
-                            )
-                    }
-                } else {
-                    if (lastStartedStep.to != toState) {
-                        return@collect
-                    }
-                    // An existing `id` means a transition is started, and calls to
-                    // `updateTransition` will control it until FINISHED or CANCELED
-                    val nextState: TransitionState
-                    val progressFraction: Float
-                    when (transitionProgress) {
-                        is CommunalTransitionProgressModel.Idle -> {
-                            if (transitionProgress.scene == toScene) {
-                                nextState = TransitionState.FINISHED
-                                progressFraction = 1f
-                            } else {
-                                nextState = TransitionState.CANCELED
-                                progressFraction = 0f
-                            }
-                        }
-                        is CommunalTransitionProgressModel.Transition -> {
-                            nextState = TransitionState.RUNNING
-                            progressFraction = transitionProgress.progress
-                        }
-                        is CommunalTransitionProgressModel.OtherTransition -> {
-                            // Shouldn't happen but if another transition starts during the
-                            // current one, mark the current one as canceled.
-                            nextState = TransitionState.CANCELED
-                            progressFraction = 0f
-                        }
-                    }
-                    transitionRepository.updateTransition(
-                        id,
-                        progressFraction,
-                        nextState,
-                    )
-
-                    if (
-                        nextState == TransitionState.CANCELED ||
-                            nextState == TransitionState.FINISHED
-                    ) {
-                        transitionId = null
-                    }
-
-                    // If canceled, just put the state back.
-                    if (nextState == TransitionState.CANCELED) {
-                        transitionRepository.startTransition(
-                            TransitionInfo(
-                                ownerName = transitionOwnerName,
-                                from = toState,
-                                to = fromState,
-                                animator =
-                                    ValueAnimator().apply {
-                                        interpolator = Interpolators.LINEAR
-                                        duration = 0
-                                    }
-                            )
-                        )
-                    }
-                }
-            }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index be814ae..3d2aafe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -440,6 +440,7 @@
             updateEndAreaColor(groupStatus.selected() ? mController.getColorSeekbarProgress()
                     : mController.getColorItemBackground());
             mEndTouchArea.setContentDescription(getDeviceItemContentDescription(device));
+            mCheckBox.setOnCheckedChangeListener(null);
             mCheckBox.setChecked(groupStatus.selected());
             mCheckBox.setOnCheckedChangeListener(
                     isEnabled ? (buttonView, isChecked) -> onGroupActionTriggered(
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 01bcc24..efdf5be 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
@@ -40,6 +40,7 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.logger.SceneLogger
+import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.composable.Overlay
 import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
@@ -218,39 +219,36 @@
      * it being a false touch.
      */
     fun canChangeScene(toScene: SceneKey): Boolean {
-        val interactionTypeOrNull =
-            when (toScene) {
-                Scenes.Bouncer -> Classifier.BOUNCER_UNLOCK
-                Scenes.Gone -> Classifier.UNLOCK
-                Scenes.Shade -> Classifier.NOTIFICATION_DRAG_DOWN
-                Scenes.QuickSettings -> Classifier.QUICK_SETTINGS
-                else -> null
-            }
-
-        val fromScene = currentScene.value
-        val isAllowed =
-            interactionTypeOrNull?.let { interactionType ->
-                // It's important that the falsing system is always queried, even if no enforcement
-                // will occur. This helps build up the right signal in the system.
-                val isFalseTouch = falsingInteractor.isFalseTouch(interactionType)
-
-                // Only enforce falsing if moving from the lockscreen scene to a new scene.
-                val fromLockscreenScene = fromScene == Scenes.Lockscreen
-
-                !fromLockscreenScene || !isFalseTouch
-            } ?: true
-
-        if (isAllowed) {
+        return isInteractionAllowedByFalsing(toScene).also {
             // A scene change is guaranteed; log it.
             logger.logSceneChanged(
-                from = fromScene,
+                from = currentScene.value,
                 to = toScene,
                 sceneState = null,
                 reason = "user interaction",
                 isInstant = false,
             )
         }
-        return isAllowed
+    }
+
+    /**
+     * Returns `true` if showing the [newlyShown] overlay is currently allowed; `false` otherwise.
+     *
+     * This is invoked only for user-initiated transitions. The goal is to check with the falsing
+     * system whether the overlay change should be rejected due to it being a false touch.
+     */
+    fun canShowOrReplaceOverlay(
+        newlyShown: OverlayKey,
+        beingReplaced: OverlayKey? = null,
+    ): Boolean {
+        return isInteractionAllowedByFalsing(newlyShown).also {
+            // An overlay change is guaranteed; log it.
+            logger.logOverlayChangeRequested(
+                from = beingReplaced,
+                to = newlyShown,
+                reason = "user interaction",
+            )
+        }
     }
 
     /**
@@ -313,6 +311,34 @@
         return sceneInteractor.filteredUserActions(unfiltered)
     }
 
+    /**
+     * Returns `true` if transitioning to [content] is permissible by the falsing system; `false`
+     * otherwise.
+     */
+    private fun isInteractionAllowedByFalsing(content: ContentKey): Boolean {
+        val interactionTypeOrNull =
+            when (content) {
+                Scenes.Bouncer -> Classifier.BOUNCER_UNLOCK
+                Scenes.Gone -> Classifier.UNLOCK
+                Scenes.Shade,
+                Overlays.NotificationsShade -> Classifier.NOTIFICATION_DRAG_DOWN
+                Scenes.QuickSettings,
+                Overlays.QuickSettingsShade -> Classifier.QUICK_SETTINGS
+                else -> null
+            }
+
+        return interactionTypeOrNull?.let { interactionType ->
+            // It's important that the falsing system is always queried, even if no enforcement
+            // will occur. This helps build up the right signal in the system.
+            val isFalseTouch = falsingInteractor.isFalseTouch(interactionType)
+
+            // Only enforce falsing if moving from the lockscreen scene to new content.
+            val fromLockscreenScene = currentScene.value == Scenes.Lockscreen
+
+            !fromLockscreenScene || !isFalseTouch
+        } ?: true
+    }
+
     /** Defines interface for classes that can handle externally-reported [MotionEvent]s. */
     interface MotionEventHandler {
         /** Notifies that a [MotionEvent] has occurred. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 89cb420..9bc5231 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -69,7 +69,10 @@
         messagingStyle.conversationType =
             if (entry.ranking.channel.isImportantConversation)
                 Notification.MessagingStyle.CONVERSATION_TYPE_IMPORTANT
-            else Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL
+            else if (entry.ranking.isConversation)
+                Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL
+            else
+                Notification.MessagingStyle.CONVERSATION_TYPE_LEGACY
         entry.ranking.conversationShortcutInfo?.let { shortcutInfo ->
             logger.logAsyncTaskProgress(entry, "getting shortcut icon")
             messagingStyle.shortcutIcon = launcherApps.getShortcutIcon(shortcutInfo)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index a2d563a..9bf0768 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -47,6 +47,7 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.IndentingPrintWriter;
@@ -81,6 +82,8 @@
 import com.android.internal.util.ContrastColorUtil;
 import com.android.internal.widget.CachingIconView;
 import com.android.internal.widget.CallLayout;
+import com.android.internal.widget.ConversationLayout;
+import com.android.internal.widget.MessagingLayout;
 import com.android.systemui.Flags;
 import com.android.systemui.flags.RefactorFlag;
 import com.android.systemui.plugins.FalsingManager;
@@ -97,6 +100,7 @@
 import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.FeedbackIcon;
 import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
+import com.android.systemui.statusbar.notification.NmSummarizationUiFlag;
 import com.android.systemui.statusbar.notification.NotificationFadeAware;
 import com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController;
 import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -205,6 +209,7 @@
     private int mMaxSmallHeightBeforeN;
     private int mMaxSmallHeightBeforeP;
     private int mMaxSmallHeightBeforeS;
+    private int mMaxSmallHeightWithSummarization;
     private int mMaxSmallHeight;
     private int mMaxExpandedHeight;
     private int mMaxExpandedHeightForPromotedOngoing;
@@ -856,6 +861,8 @@
         int smallHeight;
 
         boolean isCallLayout = contractedView instanceof CallLayout;
+        boolean isMessagingLayout = contractedView instanceof MessagingLayout
+                || contractedView instanceof ConversationLayout;
 
         if (customView && beforeS && !mIsSummaryWithChildren) {
             if (beforeN) {
@@ -867,6 +874,10 @@
             }
         } else if (isCallLayout) {
             smallHeight = maxExpandedHeight;
+        } else if (NmSummarizationUiFlag.isEnabled()
+                && isMessagingLayout
+                && !TextUtils.isEmpty(mEntry.getRanking().getSummarization())) {
+            smallHeight = mMaxSmallHeightWithSummarization;
         } else {
             smallHeight = mMaxSmallHeight;
         }
@@ -2111,6 +2122,8 @@
             mMaxSmallHeight = NotificationUtils.getFontScaledHeight(mContext,
                     R.dimen.notification_min_height);
         }
+        mMaxSmallHeightWithSummarization = NotificationUtils.getFontScaledHeight(mContext,
+                com.android.internal.R.dimen.notification_collapsed_height_with_summarization);
         mMaxExpandedHeight = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_max_height);
         mMaxExpandedHeightForPromotedOngoing = NotificationUtils.getFontScaledHeight(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 73e8246..e311b53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -55,6 +55,7 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
 import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.NmSummarizationUiFlag;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
@@ -201,13 +202,13 @@
                 mNotifLayoutInflaterFactoryProvider,
                 mHeadsUpStyleProvider,
                 mLogger);
-
         result = inflateSmartReplyViews(result, reInflateFlags, entry, row.getContext(),
                 packageContext, row.getExistingSmartReplyState(), smartRepliesInflater, mLogger);
         boolean isConversation = entry.getRanking().isConversation();
         Notification.MessagingStyle messagingStyle = null;
-        if (isConversation && (AsyncHybridViewInflation.isEnabled()
-                || LockscreenOtpRedaction.isSingleLineViewEnabled())) {
+        if (NmSummarizationUiFlag.isEnabled()
+                || (isConversation && (AsyncHybridViewInflation.isEnabled()
+                || LockscreenOtpRedaction.isSingleLineViewEnabled()))) {
             messagingStyle = mConversationProcessor
                     .processNotification(entry, builder, mLogger);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index 589e5b8..517fc3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -48,6 +48,7 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
 import com.android.systemui.statusbar.notification.InflationException
+import com.android.systemui.statusbar.notification.NmSummarizationUiFlag
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
@@ -700,7 +701,7 @@
 
             // process conversations and extract the messaging style
             val messagingStyle =
-                if (entry.ranking.isConversation) {
+                if (NmSummarizationUiFlag.isEnabled || entry.ranking.isConversation) {
                     conversationProcessor.processNotification(entry, builder, logger)
                 } else null
 
@@ -730,9 +731,8 @@
                         builder = builder,
                         systemUiContext = systemUiContext,
                         redactText = false,
-                        summarization = entry.sbn.notification.extras.getCharSequence(
-                            EXTRA_SUMMARIZED_CONTENT,
-                        )
+                        summarization =
+                            entry.sbn.notification.extras.getCharSequence(EXTRA_SUMMARIZED_CONTENT),
                     )
                 } else null
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
index d995b86..0f7366d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
@@ -38,7 +38,6 @@
             bgDispatcher = testDispatcher,
             mainDispatcher = testDispatcher,
             keyguardInteractor = keyguardInteractor,
-            glanceableHubTransitions = glanceableHubTransitions,
             communalInteractor = communalInteractor,
             communalSceneInteractor = communalSceneInteractor,
             communalSettingsInteractor = communalSettingsInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
index 494f08b..bf72e48 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
@@ -31,7 +31,6 @@
             scope = applicationCoroutineScope,
             mainDispatcher = testDispatcher,
             bgDispatcher = testDispatcher,
-            glanceableHubTransitions = glanceableHubTransitions,
             communalSettingsInteractor = communalSettingsInteractor,
             keyguardInteractor = keyguardInteractor,
             transitionRepository = keyguardTransitionRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
index ff7a06c..985044c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
@@ -39,7 +39,6 @@
             keyguardInteractor = keyguardInteractor,
             shadeRepository = shadeRepository,
             powerInteractor = powerInteractor,
-            glanceableHubTransitions = glanceableHubTransitions,
             communalSettingsInteractor = communalSettingsInteractor,
             swipeToDismissInteractor = swipeToDismissInteractor,
             keyguardOcclusionInteractor = keyguardOcclusionInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitionsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitionsKosmos.kt
deleted file mode 100644
index a45b269..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitionsKosmos.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.domain.interactor
-
-import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.glanceableHubTransitions by
-    Kosmos.Fixture {
-        GlanceableHubTransitions(
-            transitionRepository = keyguardTransitionRepository,
-            transitionInteractor = keyguardTransitionInteractor,
-            communalInteractor = communalInteractor,
-        )
-    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/ConversationNotificationManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/ConversationNotificationManagerKosmos.kt
new file mode 100644
index 0000000..af53ae56
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/ConversationNotificationManagerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2025 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.statusbar.notification
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.conversationNotificationManager: ConversationNotificationManager by
+    Kosmos.Fixture { mock<ConversationNotificationManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderLoggerKosmos.kt
new file mode 100644
index 0000000..cc40222
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderLoggerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2025 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.statusbar.notification.row
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.notificationRowContentBinderLogger: NotificationRowContentBinderLogger by
+    Kosmos.Fixture { mock<NotificationRowContentBinderLogger>() }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b48d0a6..86871ea 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -12598,7 +12598,7 @@
     //==========================================================================================
     static final int LOG_NB_EVENTS_LIFECYCLE = 20;
     static final int LOG_NB_EVENTS_PHONE_STATE = 20;
-    static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 50;
+    static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 200;
     static final int LOG_NB_EVENTS_FORCE_USE = 20;
     static final int LOG_NB_EVENTS_VOLUME = 100;
     static final int LOG_NB_EVENTS_DYN_POLICY = 10;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 34f8ac6..191c21e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8288,8 +8288,11 @@
         Preconditions.checkCallAuthorization(isSystemUid(caller));
         // Managed Profile password can only be changed when it has a separate challenge.
         if (!isSeparateProfileChallengeEnabled(userId)) {
-            Preconditions.checkCallAuthorization(!isManagedProfile(userId), "You can "
-                    + "not set the active password for a managed profile, userId = %d", userId);
+            if (isManagedProfile(userId)) {
+                Slogf.i(LOG_TAG, "You can not set the active password for a managed profile,"
+                        + " userId = %d", userId);
+                return;
+            }
         }
 
         DevicePolicyData policy = getUserData(userId);
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index 3ee6dc4..1273826 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -16,6 +16,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -24,7 +25,7 @@
 import android.os.TestLooperManager;
 import android.util.ArrayMap;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.runners.model.FrameworkMethod;
 
@@ -33,8 +34,11 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayDeque;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Queue;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -67,16 +71,38 @@
     private Handler mHandler;
     private TestLooperManager mQueueWrapper;
 
+    /**
+     * Baklava introduces new {@link TestLooperManager} APIs that we can use instead of reflection.
+     */
+    private static boolean isAtLeastBaklava() {
+        Method[] methods = TestLooperManager.class.getMethods();
+        for (Method method : methods) {
+            if (method.getName().equals("peekWhen")) {
+                return true;
+            }
+        }
+        return false;
+        // TODO(shayba): delete the above, uncomment the below.
+        // SDK_INT has not yet ramped to Baklava in all 25Q2 builds.
+        // return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA;
+    }
+
     static {
-        try {
-            MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
-            MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
-            MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
-            MESSAGE_NEXT_FIELD.setAccessible(true);
-            MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
-            MESSAGE_WHEN_FIELD.setAccessible(true);
-        } catch (NoSuchFieldException e) {
-            throw new RuntimeException("Failed to initialize TestableLooper", e);
+        if (isAtLeastBaklava()) {
+            MESSAGE_QUEUE_MESSAGES_FIELD = null;
+            MESSAGE_NEXT_FIELD = null;
+            MESSAGE_WHEN_FIELD = null;
+        } else {
+            try {
+                MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
+                MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
+                MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
+                MESSAGE_NEXT_FIELD.setAccessible(true);
+                MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
+                MESSAGE_WHEN_FIELD.setAccessible(true);
+            } catch (NoSuchFieldException e) {
+                throw new RuntimeException("Failed to initialize TestableLooper", e);
+            }
         }
     }
 
@@ -222,8 +248,61 @@
     }
 
     public void moveTimeForward(long milliSeconds) {
+        if (isAtLeastBaklava()) {
+            moveTimeForwardBaklava(milliSeconds);
+        } else {
+            moveTimeForwardLegacy(milliSeconds);
+        }
+    }
+
+    private void moveTimeForwardBaklava(long milliSeconds) {
+        // Drain all Messages from the queue.
+        Queue<Message> messages = new ArrayDeque<>();
+        while (true) {
+            Message message = mQueueWrapper.poll();
+            if (message == null) {
+                break;
+            }
+
+            // Adjust the Message's delivery time.
+            long newWhen = message.when - milliSeconds;
+            if (newWhen < 0) {
+                newWhen = 0;
+            }
+            message.when = newWhen;
+            messages.add(message);
+        }
+
+        // Repost all Messages back to the queuewith a new time.
+        while (true) {
+            Message message = messages.poll();
+            if (message == null) {
+                break;
+            }
+
+            Runnable callback = message.getCallback();
+            Handler handler = message.getTarget();
+            long when = message.getWhen();
+
+            // The Message cannot be re-enqueued because it is marked in use.
+            // Make a copy of the Message and recycle the original.
+            // This resets {@link Message#isInUse()} but retains all other content.
+            {
+                Message newMessage = Message.obtain();
+                newMessage.copyFrom(message);
+                newMessage.setCallback(callback);
+                mQueueWrapper.recycle(message);
+                message = newMessage;
+            }
+
+            // Send the Message back to its Handler to be re-enqueued.
+            handler.sendMessageAtTime(message, when);
+        }
+    }
+
+    private void moveTimeForwardLegacy(long milliSeconds) {
         try {
-            Message msg = getMessageLinkedList();
+            Message msg = (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(mLooper.getQueue());
             while (msg != null) {
                 long updatedWhen = msg.getWhen() - milliSeconds;
                 if (updatedWhen < 0) {
@@ -237,17 +316,6 @@
         }
     }
 
-    private Message getMessageLinkedList() {
-        try {
-            MessageQueue queue = mLooper.getQueue();
-            return (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(queue);
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(
-                    "Access failed in TestableLooper: get - MessageQueue.mMessages",
-                    e);
-        }
-    }
-
     private int processQueuedMessages() {
         int count = 0;
         Runnable barrierRunnable = () -> { };