Merge "Deprecate min/max size in PipResizeGestureHandler" into main
diff --git a/apct-tests/perftests/core/src/android/os/TracePerfTest.java b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
index d905124..00e1c1f 100644
--- a/apct-tests/perftests/core/src/android/os/TracePerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
@@ -147,7 +147,7 @@
                 .addField(1 /* sending_thread_name */, "foo")
                 .endNested()
                 .endProto()
-                .addTerminatingFlow(5)
+                .setTerminatingFlow(5)
                 .emit();
 
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -158,7 +158,7 @@
                     .addField(1 /* sending_thread_name */, "foo")
                     .endNested()
                     .endProto()
-                    .addTerminatingFlow(5)
+                    .setTerminatingFlow(5)
                     .emit();
         }
     }
diff --git a/apct-tests/perftests/healthconnect/OWNERS b/apct-tests/perftests/healthconnect/OWNERS
index acfe799..7c9e769 100644
--- a/apct-tests/perftests/healthconnect/OWNERS
+++ b/apct-tests/perftests/healthconnect/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 1219472
 
-arkivanov@google.com
 jstembridge@google.com
 itsleo@google.com
diff --git a/api/OWNERS b/api/OWNERS
index f2bcf13..31ffa8c 100644
--- a/api/OWNERS
+++ b/api/OWNERS
@@ -1,4 +1,3 @@
-hansson@google.com
 
 # Modularization team
 file:platform/packages/modules/common:/OWNERS
diff --git a/cmds/idmap2/OWNERS b/cmds/idmap2/OWNERS
index 062ffd4..def9f40 100644
--- a/cmds/idmap2/OWNERS
+++ b/cmds/idmap2/OWNERS
@@ -1,4 +1,3 @@
 set noparent
-toddke@google.com
 patb@google.com
 zyy@google.com
diff --git a/cmds/incidentd/OWNERS b/cmds/incidentd/OWNERS
index bcdcfc3..db8fa9f 100644
--- a/cmds/incidentd/OWNERS
+++ b/cmds/incidentd/OWNERS
@@ -1,4 +1,3 @@
 joeo@google.com
 yaochen@google.com
 yanmin@google.com
-zhouwenjie@google.com
diff --git a/core/java/android/app/DreamManager.java b/core/java/android/app/DreamManager.java
index c597a9d..555006b 100644
--- a/core/java/android/app/DreamManager.java
+++ b/core/java/android/app/DreamManager.java
@@ -71,8 +71,11 @@
     @TestApi
     @RequiresPermission(WRITE_SECURE_SETTINGS)
     public void setScreensaverEnabled(boolean enabled) {
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.SCREENSAVER_ENABLED, enabled ? 1 : 0, UserHandle.USER_CURRENT);
+        try {
+            mService.setScreensaverEnabled(enabled);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
     }
 
     /**
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 1b71e73..cc72d8f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -9526,14 +9526,21 @@
                     .viewType(viewType)
                     .highlightExpander(isConversationLayout)
                     .hideProgress(true)
-                    .text(null)
                     .hideLeftIcon(isOneToOne)
                     .hideRightIcon(hideRightIcons || isOneToOne);
             if (notificationsRedesignTemplates()) {
+                String lastMessage = !mMessages.isEmpty()
+                        ? mMessages.getLast().mText.toString() : null;
+
                 p.title(conversationTitle)
+                        // The text is not actually displayed like this (since we're using a
+                        // MessagingLinearLayout instead of the regular text), but we're using it to
+                        // know whether the notification will have a second line in practice.
+                        .text(lastMessage)
                         .hideAppName(isCollapsed);
             } else {
                 p.title(isLegacyHeaderless ? conversationTitle : null)
+                        .text(null)
                         .headerTextSecondary(isLegacyHeaderless ? null : conversationTitle);
             }
             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index e93d8bdb..3a02188 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -169,6 +169,23 @@
             "name": "CtsWindowManagerBackgroundActivityTestCases"
         }
     ],
+    // v2/sysui/suite/test-mapping-sysui-screenshot-test
+    "sysui-screenshot-test": [
+        {
+        "name": "SystemUIGoogleScreenshotTests",
+        "options": [
+            {
+            "exclude-annotation": "org.junit.Ignore"
+            },
+            {
+            "exclude-annotation": "androidx.test.filters.FlakyTest"
+            },
+            {
+            "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+            }
+        ]
+        }
+    ],
     "postsubmit": [
         {
             "file_patterns": ["(/|^)ActivityThreadClientTest.java"],
diff --git a/core/java/android/app/contentsuggestions/OWNERS b/core/java/android/app/contentsuggestions/OWNERS
index 5f8de77..3d21a6a 100644
--- a/core/java/android/app/contentsuggestions/OWNERS
+++ b/core/java/android/app/contentsuggestions/OWNERS
@@ -1,4 +1,3 @@
 # Bug component: 643919
 
 hackz@google.com
-volnov@google.com
diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig
index 1de034b..1a14b20 100644
--- a/core/java/android/app/contextualsearch/flags.aconfig
+++ b/core/java/android/app/contextualsearch/flags.aconfig
@@ -23,6 +23,18 @@
     bug: "371065456"
 }
 
+
+flag {
+  name: "report_secure_surfaces_in_assist_structure"
+  namespace: "windowing_frontend"
+  description: "SurfaceView reports when the surface is using a SECURE flag."
+  bug: "390504528"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+
 flag {
     name: "contextual_search_prevent_self_capture"
     namespace: "sysui_integrations"
diff --git a/core/java/android/app/people/OWNERS b/core/java/android/app/people/OWNERS
index 7371a88..399511a 100644
--- a/core/java/android/app/people/OWNERS
+++ b/core/java/android/app/people/OWNERS
@@ -1,4 +1,3 @@
 # Bug component: 978868
 
-danningc@google.com
-juliacr@google.com
\ No newline at end of file
+juliacr@google.com
diff --git a/core/java/android/app/wearable/OWNERS b/core/java/android/app/wearable/OWNERS
index 497eaf0..56c8ca5 100644
--- a/core/java/android/app/wearable/OWNERS
+++ b/core/java/android/app/wearable/OWNERS
@@ -2,4 +2,3 @@
 hackz@google.com
 oni@google.com
 tomchan@google.com
-volnov@google.com
\ No newline at end of file
diff --git a/core/java/android/content/integrity/OWNERS b/core/java/android/content/integrity/OWNERS
index 20c758a..ca65fda 100644
--- a/core/java/android/content/integrity/OWNERS
+++ b/core/java/android/content/integrity/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 722021
 
 toddke@android.com
-toddke@google.com
 patb@google.com
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index e645060..fd59ea9 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1347,6 +1347,33 @@
             314961188L;
 
     /**
+     * Excludes the packages the override is applied to from the camera compatibility treatment for
+     * fixed-orientation apps, which simulates running on a portrait device, in the orientation
+     * requested by the app.
+     *
+     * <p>This treatment aims to mitigate camera issues on large screens, like stretched or sideways
+     * previews. It simulates running on a portrait device by:
+     * <ul>
+     *   <li>Letterboxing the app window,
+     *   <li>Cropping the camera buffer to match the app's requested orientation,
+     *   <li>Setting the camera sensor orientation to portrait.
+     *   <li>Setting the display rotation to match the app's requested orientation, given portrait
+     *       natural orientation,
+     *   <li>Refreshes the activity to trigger new camera setup, with sandboxed values.
+     * </ul>
+     *
+     * <p>By setting this override to {@code true}, it disables the camera compatibility treatment
+     * which simulates app's requested orientation.
+     *
+     * @hide
+     */
+    @ChangeId
+    @Overridable
+    @Disabled
+    public static final long OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION =
+            398195815L;  // buganizer id
+
+    /**
      * This change id forces the packages it is applied to sandbox {@link android.view.View} API to
      * an activity bounds for:
      *
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9e91f59..49fd634 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5000,10 +5000,9 @@
      * supports the Android XR Spatial APIs. The feature version indicates the highest version of
      * the Android XR Spatial APIs supported by the device.
      *
-     * <p>Also see <a href="https://developer.android.com/xr">Getting started with Spatializing
-     * your app</a>.
+     * <p>Also see <a href="https://developer.android.com/develop/xr">Develop with the Android XR
+     * SDK</a>.
      */
-    // TODO(b/374330735): update public documentation once link content is finalized
     @FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES)
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_XR_API_SPATIAL =
diff --git a/core/java/android/content/pm/parsing/OWNERS b/core/java/android/content/pm/parsing/OWNERS
index 8049d5c..445a833 100644
--- a/core/java/android/content/pm/parsing/OWNERS
+++ b/core/java/android/content/pm/parsing/OWNERS
@@ -2,4 +2,3 @@
 
 chiuwinson@google.com
 patb@google.com
-toddke@google.com
diff --git a/core/java/android/content/pm/permission/OWNERS b/core/java/android/content/pm/permission/OWNERS
index cf7e689..8ef8474 100644
--- a/core/java/android/content/pm/permission/OWNERS
+++ b/core/java/android/content/pm/permission/OWNERS
@@ -3,6 +3,5 @@
 include platform/frameworks/base:/core/java/android/permission/OWNERS
 
 toddke@android.com
-toddke@google.com
 patb@google.com
 
diff --git a/core/java/android/hardware/serial/OWNERS b/core/java/android/hardware/serial/OWNERS
new file mode 100644
index 0000000..bc2c66a
--- /dev/null
+++ b/core/java/android/hardware/serial/OWNERS
@@ -0,0 +1,6 @@
+kkaplon@google.com
+mjel@google.com
+chominskib@google.com
+wzwonarz@google.com
+gstepniewski@google.com
+xutan@google.com
\ No newline at end of file
diff --git a/core/java/android/hardware/serial/flags/flags.aconfig b/core/java/android/hardware/serial/flags/flags.aconfig
new file mode 100644
index 0000000..d8244ba
--- /dev/null
+++ b/core/java/android/hardware/serial/flags/flags.aconfig
@@ -0,0 +1,10 @@
+package: "android.hardware.serial"
+container: "system"
+
+flag {
+    name: "enable_serial_api"
+    namespace: "serial"
+    description: "Feature flag to enable serial API"
+    bug: "369155426"
+    is_exported: true
+}
\ No newline at end of file
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 1e6469c..0ceafa0 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1515,7 +1515,7 @@
                 VERSION_CODES.VANILLA_ICE_CREAM * SDK_INT_MULTIPLIER;
 
         /**
-         * The upcoming, not yet finalized, version of Android.
+         * Android 36.0.
          */
         public static final int BAKLAVA = VERSION_CODES.BAKLAVA * SDK_INT_MULTIPLIER;
     }
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 7497234..ce1717b 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -92,11 +92,8 @@
     // queue for async messages when inserting a message at the tail.
     private int mAsyncMessageCount;
 
-    /**
-     * @hide
-     */
     private final AtomicLong mMessageCount = new AtomicLong();
-    private final Thread mThread;
+    private final String mThreadName;
     private final long mTid;
 
     /**
@@ -133,7 +130,7 @@
         mUseConcurrent = sIsProcessAllowedToUseConcurrent;
         mQuitAllowed = quitAllowed;
         mPtr = nativeInit();
-        mThread = Thread.currentThread();
+        mThreadName = Thread.currentThread().getName();
         mTid = Process.myTid();
     }
 
@@ -223,10 +220,10 @@
 
         traceMessageCount();
         PerfettoTrace.instant(PerfettoTrace.MQ_CATEGORY, "message_queue_send")
-                .addFlow(msg.mEventId.get())
+                .setFlow(msg.mEventId.get())
                 .beginProto()
                 .beginNested(2004 /* message_queue */)
-                .addField(2 /* receiving_thread_name */, mThread.getName())
+                .addField(2 /* receiving_thread_name */, mThreadName)
                 .addField(3 /* message_code */, msg.what)
                 .addField(4 /* message_delay_ms */, when - SystemClock.uptimeMillis())
                 .endNested()
@@ -237,7 +234,7 @@
     /** @hide */
     private void traceMessageCount() {
         PerfettoTrace.counter(PerfettoTrace.MQ_CATEGORY, mMessageCount.get())
-                .usingThreadCounterTrack(mTid, mThread.getName())
+                .usingThreadCounterTrack(mTid, mThreadName)
                 .emit();
     }
 
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 8cfd324..48e6249 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -451,7 +451,7 @@
      * @param executor The executor on which to run the callback.
      * @param callback The callback used to deliver state change notifications.
      *
-     * <p>@throws {@link UnsupportedOperationException} if the kernel binder driver does not support
+     * @throws {@link UnsupportedOperationException} if the kernel binder driver does not support
      * this feature.
      */
     @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 1329b90..3ff6e05 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -205,7 +205,7 @@
                 .addField(1 /* sending_thread_name */, msg.mSendingThreadName)
                 .endNested()
                 .endProto()
-                .addTerminatingFlow(msg.mEventId.get())
+                .setTerminatingFlow(msg.mEventId.get())
                 .emit();
 
         // This must be in a local variabe, in case a UI event sets the logger
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index b22d177..69e84e3 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -23,7 +23,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  *
@@ -43,7 +43,7 @@
      *
      * @hide Only for use within the system server.
      */
-    public final AtomicInteger mEventId = new AtomicInteger();
+    public final AtomicLong mEventId = new AtomicLong();
 
     /**
      * User-defined message code so that the recipient can identify
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 727dcba..a6785ba 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -78,7 +78,7 @@
 per-file PatternMatcher* = file:/PACKAGE_MANAGER_OWNERS
 
 # PermissionEnforcer
-per-file PermissionEnforcer.java = tweek@google.com, brufino@google.com
+per-file PermissionEnforcer.java = tweek@google.com
 
 # RemoteCallbackList
 per-file RemoteCallbackList.java = shayba@google.com
diff --git a/core/java/android/os/PerfettoTrackEventExtra.java b/core/java/android/os/PerfettoTrackEventExtra.java
index f4b5dfe..2848bcb 100644
--- a/core/java/android/os/PerfettoTrackEventExtra.java
+++ b/core/java/android/os/PerfettoTrackEventExtra.java
@@ -172,7 +172,6 @@
         private final Pool<FieldDouble> mFieldDoubleCache;
         private final Pool<FieldString> mFieldStringCache;
         private final Pool<FieldNested> mFieldNestedCache;
-        private final Pool<Flow> mFlowCache;
         private final Pool<Builder> mBuilderCache;
 
         private Builder() {
@@ -187,7 +186,6 @@
             mFieldDoubleCache = mExtra.mFieldDoubleCache;
             mFieldStringCache = mExtra.mFieldStringCache;
             mFieldNestedCache = mExtra.mFieldNestedCache;
-            mFlowCache = mExtra.mFlowCache;
             mBuilderCache = mExtra.mBuilderCache;
 
             mCounterInt64 = mExtra.getCounterInt64();
@@ -227,7 +225,6 @@
             mFieldStringCache.reset();
             mFieldNestedCache.reset();
             mBuilderCache.reset();
-            mFlowCache.reset();
 
             mExtra.reset();
             // Reset after on init in case the thread created builders without calling emit
@@ -325,39 +322,7 @@
         /**
          * Adds a flow with {@code id}.
          */
-        public Builder addFlow(int id) {
-            if (!mIsCategoryEnabled) {
-                return this;
-            }
-            if (DEBUG) {
-                checkParent();
-            }
-            Flow flow = mFlowCache.get(sFlowSupplier);
-            flow.setProcessFlow(id);
-            mExtra.addPerfettoPointer(flow);
-            return this;
-        }
-
-        /**
-         * Adds a terminating flow with {@code id}.
-         */
-        public Builder addTerminatingFlow(int id) {
-            if (!mIsCategoryEnabled) {
-                return this;
-            }
-            if (DEBUG) {
-                checkParent();
-            }
-            Flow flow = mFlowCache.get(sFlowSupplier);
-            flow.setProcessTerminatingFlow(id);
-            mExtra.addPerfettoPointer(flow);
-            return this;
-        }
-
-        /**
-         * Adds a flow with {@code id}.
-         */
-        public Builder setFlow(int id) {
+        public Builder setFlow(long id) {
             if (!mIsCategoryEnabled) {
                 return this;
             }
@@ -372,7 +337,7 @@
         /**
          * Adds a terminating flow with {@code id}.
          */
-        public Builder setTerminatingFlow(int id) {
+        public Builder setTerminatingFlow(long id) {
             if (!mIsCategoryEnabled) {
                 return this;
             }
@@ -670,7 +635,6 @@
     private final Pool<FieldDouble> mFieldDoubleCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
     private final Pool<FieldString> mFieldStringCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
     private final Pool<FieldNested> mFieldNestedCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
-    private final Pool<Flow> mFlowCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
     private final Pool<Builder> mBuilderCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
 
     private static final NativeAllocationRegistry sRegistry =
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 77b6d70..2736b60 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -357,4 +357,9 @@
      * This may affect dream eligibility.
      */
     public abstract void setDevicePostured(boolean isPostured);
+
+    /**
+     * Notifies PowerManager that settings have changed and that it should refresh its state.
+     */
+    public abstract void updateSettings();
 }
diff --git a/core/java/android/preference/OWNERS b/core/java/android/preference/OWNERS
index b4cb9ec..38a5abd 100644
--- a/core/java/android/preference/OWNERS
+++ b/core/java/android/preference/OWNERS
@@ -1,5 +1,4 @@
 lpf@google.com
-pavlis@google.com
 clarabayarri@google.com
 
-per-file SeekBarVolumizer.java = jmtrivi@google.com
\ No newline at end of file
+per-file SeekBarVolumizer.java = jmtrivi@google.com
diff --git a/core/java/android/print/OWNERS b/core/java/android/print/OWNERS
index ce79f5d..15f6406 100644
--- a/core/java/android/print/OWNERS
+++ b/core/java/android/print/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 47273
 
 anothermark@google.com
-kumarashishg@google.com
 bmgordon@google.com
diff --git a/core/java/android/printservice/OWNERS b/core/java/android/printservice/OWNERS
index ce79f5d..15f6406 100644
--- a/core/java/android/printservice/OWNERS
+++ b/core/java/android/printservice/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 47273
 
 anothermark@google.com
-kumarashishg@google.com
 bmgordon@google.com
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 3ca9d93..fdacd60 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -59,4 +59,6 @@
             float screenBrightnessFloat, int screenBrightnessInt,
             boolean useNormalBrightnessForDoze);
     oneway void finishSelfOneway(in IBinder token, boolean immediate);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+    void setScreensaverEnabled(boolean enabled);
 }
diff --git a/core/java/android/speech/OWNERS b/core/java/android/speech/OWNERS
index 32f4822..f228ba46 100644
--- a/core/java/android/speech/OWNERS
+++ b/core/java/android/speech/OWNERS
@@ -1,3 +1,2 @@
-volnov@google.com
 eugeniom@google.com
 schfan@google.com
diff --git a/core/java/android/text/OWNERS b/core/java/android/text/OWNERS
index 0935ffd9..b493ef7 100644
--- a/core/java/android/text/OWNERS
+++ b/core/java/android/text/OWNERS
@@ -4,7 +4,6 @@
 halilibo@google.com
 haoyuchang@google.com
 justinghan@google.com
-klippenstein@google.com
 nona@google.com
 seanmcq@google.com
 siyamed@google.com
diff --git a/core/java/android/util/apk/OWNERS b/core/java/android/util/apk/OWNERS
index 0f4e869..f267f9a 100644
--- a/core/java/android/util/apk/OWNERS
+++ b/core/java/android/util/apk/OWNERS
@@ -1,3 +1,2 @@
 include /core/java/android/content/pm/OWNERS
-cbrubaker@google.com
 mpgroover@google.com
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b98f4db..4f6c730 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -2247,6 +2247,27 @@
         t.reparent(sc, mBlastSurfaceControl).show(sc);
     }
 
+    /**
+     * Populates a {@link ViewStructure} for content capture.
+     *
+     * <p>If {@link #setSecure(boolean)} has been enabled, will add a property to the
+     * {@link android.app.assist.AssistStructure.ViewNode} to indicate that content will not
+     * be available for this part of the screen.
+     *
+     * @hide
+     */
+    @Override
+    protected void onProvideStructure(@NonNull ViewStructure structure,
+            @ViewStructureType int viewFor, int flags) {
+        super.onProvideStructure(structure, viewFor, flags);
+        if (android.app.contextualsearch.flags.Flags.reportSecureSurfacesInAssistStructure()) {
+            if ((mSurfaceFlags & SurfaceControl.SECURE) != 0) {
+                structure.getExtras().putBoolean(
+                        ViewStructure.EXTRA_CONTAINS_SECURE_LAYERS, true);
+            }
+        }
+    }
+
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 43a946a..53953a9 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -77,6 +77,19 @@
             "android.view.ViewStructure.extra.FIRST_ACTIVE_POSITION";
 
     /**
+     * Key used for confirming whether the view draws graphics containing secure layers.
+     *
+     * <p>Secure layers cannot be read back into main memory and will show up as blank regions
+     * in assist screenshots.
+     *
+     * @see android.view.SurfaceControl#SECURE
+     *
+     * @hide
+     */
+    public static final String EXTRA_CONTAINS_SECURE_LAYERS =
+            "android.view.ViewStructure.extra.CONTAINS_SECURE_LAYERS";
+
+    /**
      * Key used for writing the type of the view that generated the virtual structure of its
      * children.
      *
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index c81c2bb..a4ea64e 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -203,3 +203,14 @@
   description: "Adding animating insets types and report IME visibility at the beginning of hiding"
   bug: "393049691"
 }
+
+flag {
+    name: "lower_ime_oom_importance"
+    namespace: "input_method"
+    description: "Lower keyboard app process oom importance to PERCEPTIBLE_APP_ADJ + 1."
+    bug: "372511805"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/view/textclassifier/intent/OWNERS b/core/java/android/view/textclassifier/intent/OWNERS
index dc18514..4a5dfd8 100644
--- a/core/java/android/view/textclassifier/intent/OWNERS
+++ b/core/java/android/view/textclassifier/intent/OWNERS
@@ -2,5 +2,4 @@
 
 toki@google.com
 svetoslavganov@android.com
-svetoslavganov@google.com
 joannechung@google.com
diff --git a/core/java/android/widget/flags/notification_widget_flags.aconfig b/core/java/android/widget/flags/notification_widget_flags.aconfig
index 56a2cf7..732eabe 100644
--- a/core/java/android/widget/flags/notification_widget_flags.aconfig
+++ b/core/java/android/widget/flags/notification_widget_flags.aconfig
@@ -66,4 +66,14 @@
   metadata {
     purpose: PURPOSE_BUGFIX
   }
+}
+
+flag {
+  name: "drop_non_existing_messages"
+  namespace: "systemui"
+  description: "Drops all group and message entries that no longer exist."
+  bug: "378101061"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
\ No newline at end of file
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 42bf6d1..e74a875 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -43,6 +43,8 @@
 public enum DesktopModeFlags {
     // All desktop mode related flags to be overridden by developer option toggle will be added here
     // go/keep-sorted start
+    DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX(
+            Flags::disableDesktopLaunchParamsOutsideDesktopBugFix, false),
     DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true),
     ENABLE_ACCESSIBLE_CUSTOM_HEADERS(Flags::enableAccessibleCustomHeaders, true),
     ENABLE_APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true),
@@ -113,6 +115,7 @@
     ENABLE_START_LAUNCH_TRANSITION_FROM_TASKBAR_BUGFIX(
             Flags::enableStartLaunchTransitionFromTaskbarBugfix, true),
     ENABLE_TASKBAR_OVERFLOW(Flags::enableTaskbarOverflow, false),
+    ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION(Flags::enableTaskbarRecentsLayoutTransition, false),
     ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS(Flags::enableTaskResizingKeyboardShortcuts, true),
     ENABLE_TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true),
     ENABLE_THEMED_APP_HEADERS(Flags::enableThemedAppHeaders, true),
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 684f320..355a87d 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -100,6 +100,17 @@
 }
 
 flag {
+    name: "disable_desktop_launch_params_outside_desktop_bug_fix"
+    namespace: "lse_desktop_experience"
+    description: "Prevents DesktopModeLaunchParamsModifier from modifying launch params for non /n"
+                 "desktop launches."
+    bug: "396108436"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "enable_desktop_windowing_wallpaper_activity"
     namespace: "lse_desktop_experience"
     description: "Enables desktop wallpaper activity to show wallpaper in the desktop mode"
@@ -812,6 +823,7 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
 flag {
     name: "enable_drag_to_desktop_incoming_transitions_bugfix"
     namespace: "lse_desktop_experience"
@@ -821,3 +833,23 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "enable_taskbar_recents_layout_transition"
+    namespace: "lse_desktop_experience"
+    description: "Enable Taskbar LayoutTransition for Recent Apps"
+    bug: "343521765"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "enable_desktop_opening_deeplink_minimize_animation_bugfix"
+    namespace: "lse_desktop_experience"
+    description: "Enabling a minimize animation when a new window is opened via deeplink and the Desktop Windowing open windows limit is reached."
+    bug: "360329773"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index dee1d8c..0e19eb2 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -471,4 +471,15 @@
     metadata {
         purpose: PURPOSE_BUGFIX
     }
+}
+
+flag {
+    name: "reduce_task_snapshot_memory_usage"
+    namespace: "windowing_frontend"
+    description: "Reduce task snapshot memory usage in either heap and dmabuf."
+    bug: "238206323"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
 }
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/MediaRouteControllerContentManager.java b/core/java/com/android/internal/app/MediaRouteControllerContentManager.java
index 3a8b94f..11093f1 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerContentManager.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerContentManager.java
@@ -206,6 +206,13 @@
         mDelegate.dismissView();
     }
 
+    /**
+     * Request the media route to update volume.
+     */
+    public void requestUpdateRouteVolume(int direction) {
+        mRoute.requestUpdateVolume(direction);
+    }
+
     private boolean isVolumeControlAvailable() {
         return mRoute.getVolumeHandling() == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
     }
diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialog.java b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
index 5899963..73f9515 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
@@ -42,19 +42,11 @@
  */
 public class MediaRouteControllerDialog extends AlertDialog implements
         MediaRouteControllerContentManager.Delegate {
-    // TODO(b/360050020): Eventually these 2 variables should be in the content manager instead of
-    //  here. So these should be removed when the migration is completed.
-    private final MediaRouter mRouter;
-    private final MediaRouter.RouteInfo mRoute;
-
     private final MediaRouteControllerContentManager mContentManager;
 
     public MediaRouteControllerDialog(Context context, int theme) {
         super(context, theme);
-
         mContentManager = new MediaRouteControllerContentManager(context, this);
-        mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
-        mRoute = mRouter.getSelectedRoute();
     }
 
     @Override
@@ -91,7 +83,8 @@
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
                 || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
-            mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1);
+            mContentManager.requestUpdateRouteVolume(
+                    keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1);
             return true;
         }
         return super.onKeyDown(keyCode, event);
diff --git a/core/java/com/android/internal/infra/OWNERS b/core/java/com/android/internal/infra/OWNERS
index 4550358..e69de29 100644
--- a/core/java/com/android/internal/infra/OWNERS
+++ b/core/java/com/android/internal/infra/OWNERS
@@ -1,6 +0,0 @@
-per-file AndroidFuture.java = eugenesusla@google.com
-per-file RemoteStream.java = eugenesusla@google.com
-per-file PerUser.java = eugenesusla@google.com
-per-file ServiceConnector.java = eugenesusla@google.com
-per-file AndroidFuture.aidl = eugenesusla@google.com
-per-file IAndroidFuture.aidl = eugenesusla@google.com
\ No newline at end of file
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 056a0e8..81ca231 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -789,7 +789,7 @@
      */
     public boolean readFragmentToParcel(Parcel out, BatteryHistoryFragment fragment) {
         byte[] data = mStore.readFragment(fragment);
-        if (data == null) {
+        if (data == null || data.length == 0) {
             return false;
         }
         out.unmarshall(data, 0, data.length);
@@ -934,6 +934,10 @@
                     continue;
                 }
 
+                if (data.length == 0) {
+                    continue;
+                }
+
                 out.writeBoolean(true);
                 if (useBlobs) {
                     out.writeBlob(data, 0, data.length);
@@ -976,9 +980,11 @@
                 return false;
             }
 
-            parcel.unmarshall(data, 0, data.length);
-            parcel.setDataPosition(0);
-            readHistoryBuffer(parcel);
+            if (data.length > 0) {
+                parcel.unmarshall(data, 0, data.length);
+                parcel.setDataPosition(0);
+                readHistoryBuffer(parcel);
+            }
         } catch (Exception e) {
             Slog.e(TAG, "Error reading battery history", e);
             reset();
diff --git a/core/java/com/android/internal/protolog/WmProtoLogGroups.java b/core/java/com/android/internal/protolog/WmProtoLogGroups.java
index 5edc2fb..b8b70b16 100644
--- a/core/java/com/android/internal/protolog/WmProtoLogGroups.java
+++ b/core/java/com/android/internal/protolog/WmProtoLogGroups.java
@@ -30,6 +30,8 @@
     WM_ERROR(true, true, true, Consts.TAG_WM),
     WM_DEBUG_ORIENTATION(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
             Consts.TAG_WM),
+    WM_DEBUG_ORIENTATION_CHANGE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+            Consts.TAG_WM),
     WM_DEBUG_FOCUS_LIGHT(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
             Consts.TAG_WM),
     WM_DEBUG_BOOT(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
index 9be8ea7..d174fe3 100644
--- a/core/java/com/android/internal/util/OWNERS
+++ b/core/java/com/android/internal/util/OWNERS
@@ -1,8 +1,8 @@
-per-file AsyncChannel* = lorenzo@google.com, satk@google.com, etancohen@google.com
+per-file AsyncChannel* = lorenzo@google.com, satk@google.com
 per-file MessageUtils*, Protocol*, RingBuffer*, TokenBucket* = jchalard@google.com, lorenzo@google.com, satk@google.com
 per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file *ContrastColor* = file:/services/core/java/com/android/server/notification/OWNERS
-per-file Protocol* = etancohen@google.com, lorenzo@google.com
+per-file Protocol* =lorenzo@google.com
 per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
 per-file *Dump* = file:/core/java/com/android/internal/util/dump/OWNERS
 per-file *Screenshot* = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/java/com/android/internal/util/function/pooled/OWNERS b/core/java/com/android/internal/util/function/pooled/OWNERS
index da723b3..e69de29 100644
--- a/core/java/com/android/internal/util/function/pooled/OWNERS
+++ b/core/java/com/android/internal/util/function/pooled/OWNERS
@@ -1 +0,0 @@
-eugenesusla@google.com
\ No newline at end of file
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index ce46da1..2cca3db 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -86,6 +86,7 @@
     public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
     public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
     public static final Interpolator OVERSHOOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
+    private static final int MAX_SUMMARIZATION_LINES = 3;
     public static final int IMPORTANCE_ANIM_GROW_DURATION = 250;
     public static final int IMPORTANCE_ANIM_SHRINK_DURATION = 200;
     public static final int IMPORTANCE_ANIM_SHRINK_DELAY = 25;
@@ -401,7 +402,7 @@
     public void setIsCollapsed(boolean isCollapsed) {
         mIsCollapsed = isCollapsed;
         mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed
-                ? TextUtils.isEmpty(mSummarizedContent) ? 1 : 2
+                ? TextUtils.isEmpty(mSummarizedContent) ? 1 : MAX_SUMMARIZATION_LINES
                 : Integer.MAX_VALUE);
         updateExpandButton();
         updateContentEndPaddings();
@@ -1188,6 +1189,12 @@
             }
             newGroup.setMessages(group);
         }
+
+        if (Flags.dropNonExistingMessages()) {
+            // remove groups from mAddedGroups when they are no longer in mGroups.
+            mAddedGroups.removeIf(
+                    messagingGroup -> !mGroups.contains(messagingGroup));
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index b9a603cc..b31a200 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -569,6 +569,10 @@
         mIsolatedMessage = isolatedMessage;
         updateImageContainerVisibility();
         mMessages = group;
+        if (android.widget.flags.Flags.dropNonExistingMessages()) {
+            // remove messages from mAddedMessages when they are no longer in mMessages.
+            mAddedMessages.removeIf(message -> !mMessages.contains(message));
+        }
         updateMessageColor();
     }
 
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index eb22e7c..9fe2de8 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -44,6 +44,7 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
+import android.widget.flags.Flags;
 
 import com.android.internal.R;
 
@@ -62,6 +63,7 @@
     public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
     public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
     public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+    private static final int MAX_SUMMARIZATION_LINES = 3;
     public static final OnLayoutChangeListener MESSAGING_PROPERTY_ANIMATOR
             = new MessagingPropertyAnimator();
     private final PeopleHelper mPeopleHelper = new PeopleHelper();
@@ -222,7 +224,7 @@
         List<MessagingMessage> newMessagingMessages;
         mSummarizedContent = extras.getCharSequence(Notification.EXTRA_SUMMARIZED_CONTENT);
         if (!TextUtils.isEmpty(mSummarizedContent) && mIsCollapsed) {
-            mMessagingLinearLayout.setMaxDisplayedLines(2);
+            mMessagingLinearLayout.setMaxDisplayedLines(MAX_SUMMARIZATION_LINES);
             Notification.MessagingStyle.Message summary =
                     new Notification.MessagingStyle.Message(mSummarizedContent,  0, "");
             newMessagingMessages = createMessages(List.of(summary), false, usePrecomputedText);
@@ -559,6 +561,12 @@
             }
             newGroup.setMessages(group);
         }
+
+        if (Flags.dropNonExistingMessages()) {
+            // remove groups from mAddedGroups when they are no longer in mGroups.
+            mAddedGroups.removeIf(
+                    messagingGroup -> !mGroups.contains(messagingGroup));
+        }
     }
 
     private void findGroups(List<MessagingMessage> historicMessages,
diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java
index c96e979..1b29b7f 100644
--- a/core/java/com/android/internal/widget/NotificationRowIconView.java
+++ b/core/java/com/android/internal/widget/NotificationRowIconView.java
@@ -225,6 +225,6 @@
         boolean shouldShowAppIcon();
 
         /** Get the app icon for this notification. */
-        Drawable getAppIcon();
+        @Nullable Drawable getAppIcon();
     }
 }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 92a841f..748c5b4 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -21,7 +21,6 @@
     config_namespace: "ANDROID",
     bool_variables: [
         "release_binder_death_recipient_weak_from_jni",
-        "release_package_libandroid_runtime_punch_holes",
     ],
     properties: [
         "cflags",
@@ -66,9 +65,6 @@
         release_binder_death_recipient_weak_from_jni: {
             cflags: ["-DBINDER_DEATH_RECIPIENT_WEAK_FROM_JNI"],
         },
-        release_package_libandroid_runtime_punch_holes: {
-            cflags: ["-DENABLE_PUNCH_HOLES"],
-        },
     },
 
     cpp_std: "gnu++20",
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 06fd80e..14132e6 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -236,7 +236,6 @@
         return INSTALL_FAILED_CONTAINER_ERROR;
     }
 
-#ifdef ENABLE_PUNCH_HOLES
     // punch extracted elf files as well. This will fail where compression is on (like f2fs) but it
     // will be useful for ext4 based systems
     struct statfs64 fsInfo;
@@ -253,7 +252,6 @@
                   zipFile->getZipFileName());
         }
     }
-#endif // ENABLE_PUNCH_HOLES
 
     ALOGV("Successfully moved %s to %s\n", localTmpFileName, localFileName);
 
@@ -332,7 +330,6 @@
             return INSTALL_FAILED_INVALID_APK;
         }
 
-#ifdef ENABLE_PUNCH_HOLES
         // if library is uncompressed, punch hole in it in place
         if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
             ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
@@ -345,7 +342,6 @@
         if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) {
             ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName());
         }
-#endif // ENABLE_PUNCH_HOLES
 
         return INSTALL_SUCCEEDED;
     }
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index aa8f841..c804024 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -5,7 +5,6 @@
 singhtejinder@google.com
 yanmin@google.com
 yaochen@google.com
-zhouwenjie@google.com
 
 # Frameworks
 ogunwale@google.com
diff --git a/core/res/OWNERS b/core/res/OWNERS
index faed4d8..a208f7f 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -9,7 +9,6 @@
 ilyamaty@google.com
 jbolinger@google.com
 jsharkey@android.com
-jsharkey@google.com
 juliacr@google.com
 kchyn@google.com
 michaelwr@google.com
diff --git a/core/res/res/values/locale_config.xml b/core/res/res/values/locale_config.xml
index 77d2e87..0efa1bc 100644
--- a/core/res/res/values/locale_config.xml
+++ b/core/res/res/values/locale_config.xml
@@ -163,15 +163,18 @@
         <item>en-CM</item> <!-- English (Cameroon) -->
         <item>en-CX</item> <!-- English (Christmas Island) -->
         <item>en-CY</item> <!-- English (Cyprus) -->
+        <item>en-CZ</item> <!-- English (Czechia) -->
         <item>en-DE</item> <!-- English (Germany) -->
         <item>en-DG</item> <!-- English (Diego Garcia) -->
         <item>en-DK</item> <!-- English (Denmark) -->
         <item>en-DM</item> <!-- English (Dominica) -->
         <item>en-ER</item> <!-- English (Eritrea) -->
+        <item>en-ES</item> <!-- English (Spain) -->
         <item>en-FI</item> <!-- English (Finland) -->
         <item>en-FJ</item> <!-- English (Fiji) -->
         <item>en-FK</item> <!-- English (Falkland Islands (Islas Malvinas)) -->
         <item>en-FM</item> <!-- English (Micronesia) -->
+        <item>en-FR</item> <!-- English (France) -->
         <item>en-GB</item> <!-- English (United Kingdom) -->
         <item>en-GD</item> <!-- English (Grenada) -->
         <item>en-GG</item> <!-- English (Guernsey) -->
@@ -181,12 +184,14 @@
         <item>en-GU</item> <!-- English (Guam) -->
         <item>en-GY</item> <!-- English (Guyana) -->
         <item>en-HK</item> <!-- English (Hong Kong) -->
+        <item>en-HU</item> <!-- English (Hungary) -->
         <item>en-ID</item> <!-- English (Indonesia) -->
         <item>en-IE</item> <!-- English (Ireland) -->
         <item>en-IL</item> <!-- English (Israel) -->
         <item>en-IM</item> <!-- English (Isle of Man) -->
         <item>en-IN</item> <!-- English (India) -->
         <item>en-IO</item> <!-- English (British Indian Ocean Territory) -->
+        <item>en-IT</item> <!-- English (Italy) -->
         <item>en-JE</item> <!-- English (Jersey) -->
         <item>en-JM</item> <!-- English (Jamaica) -->
         <item>en-KE</item> <!-- English (Kenya) -->
@@ -210,15 +215,19 @@
         <item>en-NF</item> <!-- English (Norfolk Island) -->
         <item>en-NG</item> <!-- English (Nigeria) -->
         <item>en-NL</item> <!-- English (Netherlands) -->
+        <item>en-NO</item> <!-- English (Norway) -->
         <item>en-NR</item> <!-- English (Nauru) -->
         <item>en-NU</item> <!-- English (Niue) -->
         <item>en-NZ</item> <!-- English (New Zealand) -->
         <item>en-PG</item> <!-- English (Papua New Guinea) -->
         <item>en-PH</item> <!-- English (Philippines) -->
         <item>en-PK</item> <!-- English (Pakistan) -->
+        <item>en-PL</item> <!-- English (Poland) -->
         <item>en-PN</item> <!-- English (Pitcairn Islands) -->
         <item>en-PR</item> <!-- English (Puerto Rico) -->
+        <item>en-PT</item> <!-- English (Portugal) -->
         <item>en-PW</item> <!-- English (Palau) -->
+        <item>en-RO</item> <!-- English (Romania) -->
         <item>en-RW</item> <!-- English (Rwanda) -->
         <item>en-SB</item> <!-- English (Solomon Islands) -->
         <item>en-SC</item> <!-- English (Seychelles) -->
@@ -227,6 +236,7 @@
         <item>en-SG</item> <!-- English (Singapore) -->
         <item>en-SH</item> <!-- English (St. Helena) -->
         <item>en-SI</item> <!-- English (Slovenia) -->
+        <item>en-SK</item> <!-- English (Slovakia) -->
         <item>en-SL</item> <!-- English (Sierra Leone) -->
         <item>en-SS</item> <!-- English (South Sudan) -->
         <item>en-SX</item> <!-- English (Sint Maarten) -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6a83bae..cb3dfc7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5063,8 +5063,6 @@
     <!-- Notification action button. Click it will open the bluetooth device details page for this hearing device. It will be shown when making a phone call with the hearing device. [CHAR LIMIT=none] -->
     <string name="hearing_device_notification_settings_button">Settings</string>
 
-    <!-- Text spoken when the current user is switched if accessibility is enabled. [CHAR LIMIT=none] -->
-    <string name="user_switched">Current user <xliff:g id="name" example="Bob">%1$s</xliff:g>.</string>
     <!-- Message shown when switching to a user [CHAR LIMIT=none] -->
     <string name="user_switching_message">Switching to <xliff:g id="name" example="Bob">%1$s</xliff:g>\u2026</string>
     <!-- Message when logging out a user on a split user system -->
diff --git a/core/tests/coretests/src/android/content/pm/OWNERS b/core/tests/coretests/src/android/content/pm/OWNERS
index 8673365..c4c40dc 100644
--- a/core/tests/coretests/src/android/content/pm/OWNERS
+++ b/core/tests/coretests/src/android/content/pm/OWNERS
@@ -1,5 +1,4 @@
 include /core/java/android/content/pm/OWNERS
 
 per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
-per-file SigningDetailsTest.java = cbrubaker@google.com
 per-file SigningDetailsTest.java = mpgroover@google.com
diff --git a/core/tests/featureflagtests/OWNERS b/core/tests/featureflagtests/OWNERS
index 2ff4f5a..6784f28 100644
--- a/core/tests/featureflagtests/OWNERS
+++ b/core/tests/featureflagtests/OWNERS
@@ -1,2 +1 @@
-sbasi@google.com
-tmfang@google.com
\ No newline at end of file
+tmfang@google.com
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index 712042f..1251fce 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -1,6 +1,5 @@
 include /PACKAGE_MANAGER_OWNERS
 
-cbrubaker@google.com
 hackbod@android.com
 hackbod@google.com
 jeffv@google.com
diff --git a/data/keyboards/Vendor_0957_Product_0031.kl b/data/keyboards/Vendor_0957_Product_0031.kl
index b47ee58..dd9fbe5 100644
--- a/data/keyboards/Vendor_0957_Product_0031.kl
+++ b/data/keyboards/Vendor_0957_Product_0031.kl
@@ -15,7 +15,7 @@
 # Key Layout file for Google Reference RCU Remote with customizable button.
 #
 
-key 116   TV_POWER      WAKE
+key 116   POWER         WAKE
 key 217   ASSIST        WAKE
 key 423   MACRO_1       WAKE
 
diff --git a/drm/java/android/drm/OWNERS b/drm/java/android/drm/OWNERS
index 4387100..b65cce7 100644
--- a/drm/java/android/drm/OWNERS
+++ b/drm/java/android/drm/OWNERS
@@ -1,4 +1,3 @@
 # Bug component: 49079
 
-jtinker@google.com
 robertshih@google.com
diff --git a/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml b/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml
index f6256e6..ed5b339 100644
--- a/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml
+++ b/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml
@@ -72,7 +72,7 @@
                     android:layout_height="wrap_content"
                     android:textSize="12sp"
                     android:textFontWeight="400"
-                    android:lineHeight="16dp"
+                    android:lineHeight="28dp"
                     android:layout_gravity="center_horizontal"
                     android:layout_marginBottom="16dp"
                     android:textColor="@androidprv:color/materialColorOnSurfaceVariant"
@@ -113,12 +113,13 @@
                 <Button
                     android:id="@+id/open_by_default_settings_dialog_confirm_button"
                     android:layout_width="wrap_content"
-                    android:layout_height="36dp"
+                    android:layout_height="wrap_content"
+                    android:minHeight="48dp"
                     android:text="@string/open_by_default_dialog_dismiss_button_text"
                     android:layout_gravity="end"
                     android:layout_marginHorizontal="24dp"
-                    android:layout_marginTop="32dp"
-                    android:layout_marginBottom="24dp"
+                    android:layout_marginTop="26dp"
+                    android:layout_marginBottom="18dp"
                     android:textSize="14sp"
                     android:textFontWeight="500"
                     android:textColor="@androidprv:color/materialColorOnPrimary"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
index 6207e5b0..7e55786 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
@@ -4,5 +4,4 @@
 mattsziklay@google.com
 mdehaini@google.com
 pbdr@google.com
-tkachenkoi@google.com
 vaniadesmonda@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
index aa50772..e3a71a1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
@@ -18,6 +18,7 @@
 
 import android.app.ActivityTaskManager.INVALID_TASK_ID
 import android.window.DesktopExperienceFlags
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.transition.FocusTransitionObserver
@@ -188,8 +189,12 @@
                 pw.println("Error: task id should be an integer")
                 return false
             }
-        pw.println("Not implemented.")
-        return false
+        controller.moveTaskToFront(
+            /* taskId= */ taskId,
+            /* remoteTransition= */ null,
+            /* unminimizeReason= */ UnminimizeReason.UNKNOWN,
+        )
+        return true
     }
 
     private fun runMoveTaskOutOfDesk(args: Array<String>, pw: PrintWriter): Boolean {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 644c5b0..7e63250 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -472,15 +472,14 @@
         remoteTransition: RemoteTransition? = null,
         callback: IMoveToDesktopCallback? = null,
     ): Boolean {
-        val runningTask = shellTaskOrganizer.getRunningTaskInfo(taskId)
-        val backgroundTask = recentTasksController?.findTaskInBackground(taskId)
-        if (runningTask == null && backgroundTask == null) {
+        val task =
+            shellTaskOrganizer.getRunningTaskInfo(taskId)
+                ?: recentTasksController?.findTaskInBackground(taskId)
+        if (task == null) {
             logW("moveTaskToDefaultDeskAndActivate taskId=%d not found", taskId)
             return false
         }
-        // TODO(342378842): Instead of using default display, support multiple displays
-        val displayId = runningTask?.displayId ?: DEFAULT_DISPLAY
-        val deskId = getDefaultDeskId(displayId)
+        val deskId = getDefaultDeskId(task.displayId)
         return moveTaskToDesk(
             taskId = taskId,
             deskId = deskId,
@@ -532,14 +531,14 @@
         remoteTransition: RemoteTransition? = null,
         callback: IMoveToDesktopCallback? = null,
     ): Boolean {
-        if (recentTasksController?.findTaskInBackground(taskId) == null) {
+        val task = recentTasksController?.findTaskInBackground(taskId)
+        if (task == null) {
             logW("moveBackgroundTaskToDesktop taskId=%d not found", taskId)
             return false
         }
         logV("moveBackgroundTaskToDesktop with taskId=%d", taskId)
-        // TODO(342378842): Instead of using default display, support multiple displays
         val taskIdToMinimize =
-            bringDesktopAppsToFrontBeforeShowingNewTask(DEFAULT_DISPLAY, wct, taskId)
+            bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, taskId)
         val exitResult =
             desktopImmersiveController.exitImmersiveIfApplicable(
                 wct = wct,
@@ -1122,12 +1121,13 @@
                 excludeTaskId = launchingTaskId,
                 reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
             )
-        var deskIdToActivate: Int? = null
-        if (
-            DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue &&
+        var activationRunOnTransitStart: RunOnTransitStart? = null
+        val shouldActivateDesk =
+            (DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue ||
+                DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) &&
                 !isDesktopModeShowing(displayId)
-        ) {
-            deskIdToActivate =
+        if (shouldActivateDesk) {
+            val deskIdToActivate =
                 checkNotNull(
                     launchingTaskId?.let { taskRepository.getDeskIdForTask(it) }
                         ?: getDefaultDeskId(displayId)
@@ -1137,6 +1137,18 @@
             // Desk activation must be handled before app launch-related transactions.
             activateDeskWct.merge(launchTransaction, /* transfer= */ true)
             launchTransaction = activateDeskWct
+            activationRunOnTransitStart = { transition ->
+                desksTransitionObserver.addPendingTransition(
+                    DeskTransition.ActivateDesk(
+                        token = transition,
+                        displayId = displayId,
+                        deskId = deskIdToActivate,
+                    )
+                )
+            }
+            desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
+                FREEFORM_ANIMATION_DURATION
+            )
         }
         val t =
             if (remoteTransition == null) {
@@ -1170,24 +1182,7 @@
         if (launchingTaskId != null && taskRepository.isMinimizedTask(launchingTaskId)) {
             addPendingUnminimizeTransition(t, displayId, launchingTaskId, unminimizeReason)
         }
-        if (
-            DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue &&
-                deskIdToActivate != null
-        ) {
-            if (DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue) {
-                desksTransitionObserver.addPendingTransition(
-                    DeskTransition.ActivateDesk(
-                        token = t,
-                        displayId = displayId,
-                        deskId = deskIdToActivate,
-                    )
-                )
-            }
-
-            desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
-                FREEFORM_ANIMATION_DURATION
-            )
-        }
+        activationRunOnTransitStart?.invoke(t)
         exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
         return t
     }
@@ -1321,25 +1316,23 @@
             applyFreeformDisplayChange(wct, task, displayId)
         }
 
-        val activationRunnable: RunOnTransitStart?
         if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
             desksOrganizer.moveTaskToDesk(wct, destinationDeskId, task)
-            prepareForDeskActivation(displayId, wct)
-            desksOrganizer.activateDesk(wct, destinationDeskId)
-            activationRunnable = { transition ->
-                desksTransitionObserver.addPendingTransition(
-                    DeskTransition.ActiveDeskWithTask(
-                        token = transition,
-                        displayId = displayId,
-                        deskId = destinationDeskId,
-                        enterTaskId = task.taskId,
-                    )
-                )
-            }
         } else {
             wct.reparent(task.token, displayAreaInfo.token, /* onTop= */ true)
-            activationRunnable = null
         }
+        addDeskActivationChanges(destinationDeskId, wct)
+        val activationRunnable: RunOnTransitStart = { transition ->
+            desksTransitionObserver.addPendingTransition(
+                DeskTransition.ActiveDeskWithTask(
+                    token = transition,
+                    displayId = displayId,
+                    deskId = destinationDeskId,
+                    enterTaskId = task.taskId,
+                )
+            )
+        }
+
         if (Flags.enableDisplayFocusInShellTransitions()) {
             // Bring the destination display to top with includingParents=true, so that the
             // destination display gains the display focus, which makes the top task in the display
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
index 44d46ee..7a63ec5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
@@ -2,7 +2,6 @@
 atsjenk@google.com
 jorgegil@google.com
 madym@google.com
-nmusgrave@google.com
 pbdr@google.com
 vaniadesmonda@google.com
 pragyabajoria@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
index 5aa3c4e..245669b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
@@ -1,3 +1,2 @@
 # WM shell sub-module TV pip owner
-galinap@google.com
 bronger@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 0438d16..9302347 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -93,6 +93,7 @@
 import android.app.ActivityOptions;
 import android.app.IActivityTaskManager;
 import android.app.PendingIntent;
+import android.app.PictureInPictureParams;
 import android.app.TaskInfo;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
@@ -2890,6 +2891,16 @@
                     prepareEnterSplitScreen(out);
                     mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
                             TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering);
+                } else if (isSplitScreenVisible() && isOpening) {
+                    // launching into an existing split stage; possibly launchAdjacent
+                    // If we're replacing a pip-able app, we need to let mixed handler take care of
+                    // it. Otherwise we'll just treat it as an enter+resize
+                    if (mSplitLayout.calculateCurrentSnapPosition() != SNAP_TO_2_50_50) {
+                        // updated layout will get applied in startAnimation pendingResize
+                        mSplitTransitions.setEnterTransition(transition,
+                                request.getRemoteTransition(),
+                                TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, true /*resizeAnim*/);
+                    }
                 } else if (inFullscreen && isSplitScreenVisible()) {
                     // If the trigger task is in fullscreen and in split, exit split and place
                     // task on top
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS
index 28be0ef..9dc0ebb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS
@@ -1,3 +1,2 @@
 # WM shell sub-module TV splitscreen owner
-galinap@google.com
 bronger@google.com
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 8805071..b0785df 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -334,6 +334,16 @@
         whenever(enterDesktopTransitionHandler.moveToDesktop(any(), any())).thenAnswer { Binder() }
         whenever(exitDesktopTransitionHandler.startTransition(any(), any(), any(), any()))
             .thenReturn(Binder())
+        whenever(
+                desktopMixedTransitionHandler.startLaunchTransition(
+                    any(),
+                    any(),
+                    anyOrNull(),
+                    anyOrNull(),
+                    anyOrNull(),
+                )
+            )
+            .thenReturn(Binder())
         whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
         whenever(displayController.getDisplayContext(anyInt())).thenReturn(mockDisplayContext)
         whenever(displayController.getDisplay(anyInt())).thenReturn(display)
@@ -1616,7 +1626,7 @@
     @Test
     @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun moveTaskToDesktop_desktopWallpaperDisabled_nonRunningTask_launchesInFreeform() {
-        val task = createTaskInfo(1)
+        val task = createRecentTaskInfo(1)
         whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
         whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
 
@@ -1631,7 +1641,7 @@
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun moveTaskToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() {
         whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
-        val task = createTaskInfo(1)
+        val task = createRecentTaskInfo(1)
         whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
         whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
 
@@ -1803,7 +1813,7 @@
         whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
             .thenReturn(Binder())
 
-        val task = createTaskInfo(1)
+        val task = createRecentTaskInfo(1)
         whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
         whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
         controller.moveTaskToDefaultDeskAndActivate(
@@ -1818,6 +1828,34 @@
     }
 
     @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+        Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
+        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+    )
+    fun moveBackgroundTaskToDesktop_nonDefaultDisplay_reordersHomeAndWallpaperOfNonDefaultDisplay() {
+        val homeTask = setUpHomeTask(displayId = SECOND_DISPLAY)
+        val wallpaperToken = MockToken().token()
+        whenever(desktopWallpaperActivityTokenProvider.getToken(SECOND_DISPLAY))
+            .thenReturn(wallpaperToken)
+        val task = setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = 2, background = true)
+
+        controller.moveTaskToDefaultDeskAndActivate(
+            taskId = task.taskId,
+            transitionSource = UNKNOWN,
+            remoteTransition = RemoteTransition(spy(TestRemoteTransition())),
+        )
+
+        val wct = getLatestTransition()
+        val homeReorderIndex = wct.indexOfReorder(homeTask, toTop = true)
+        val wallpaperReorderIndex = wct.indexOfReorder(wallpaperToken, toTop = true)
+        assertThat(homeReorderIndex).isNotEqualTo(-1)
+        assertThat(wallpaperReorderIndex).isNotEqualTo(-1)
+        // Wallpaper last, to be in front of Home.
+        assertThat(wallpaperReorderIndex).isGreaterThan(homeReorderIndex)
+    }
+
+    @Test
     fun moveRunningTaskToDesktop_remoteTransition_usesOneShotHandler() {
         val transitionHandlerArgCaptor = argumentCaptor<TransitionHandler>()
         whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
@@ -2468,7 +2506,7 @@
 
     @Test
     fun moveTaskToFront_backgroundTask_launchesTask() {
-        val task = createTaskInfo(1)
+        val task = createRecentTaskInfo(1)
         whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
         whenever(
                 desktopMixedTransitionHandler.startLaunchTransition(
@@ -2490,7 +2528,7 @@
     @Test
     fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_minimizesBackTask() {
         val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
-        val task = createTaskInfo(1001)
+        val task = createRecentTaskInfo(1001)
         whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
         whenever(
                 desktopMixedTransitionHandler.startLaunchTransition(
@@ -2786,6 +2824,73 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveToNextDisplay_toDesktopInOtherDisplay_bringsExistingTasksToFront() {
+        val transition = Binder()
+        val sourceDeskId = 0
+        val targetDeskId = 2
+        taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+        taskRepository.setDeskInactive(deskId = targetDeskId)
+        // Set up two display ids
+        whenever(rootTaskDisplayAreaOrganizer.displayIds)
+            .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+        // Create a mock for the target display area: second display
+        val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+            .thenReturn(secondDisplayArea)
+        whenever(transitions.startTransition(eq(TRANSIT_CHANGE), any(), anyOrNull()))
+            .thenReturn(transition)
+        val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = sourceDeskId)
+        val task2 = setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+
+        controller.moveToNextDisplay(task1.taskId)
+
+        // Existing desktop task in the target display is moved to front.
+        val wct = getLatestTransition()
+        wct.assertReorder(task2.token, /* toTop= */ true)
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+        Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
+        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+    )
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveToNextDisplay_toDesktopInOtherDisplay_movesHomeAndWallpaperToFront() {
+        val homeTask = setUpHomeTask(displayId = SECOND_DISPLAY)
+        whenever(desktopWallpaperActivityTokenProvider.getToken(SECOND_DISPLAY))
+            .thenReturn(wallpaperToken)
+        val transition = Binder()
+        val sourceDeskId = 0
+        val targetDeskId = 2
+        taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+        taskRepository.setDeskInactive(deskId = targetDeskId)
+        // Set up two display ids
+        whenever(rootTaskDisplayAreaOrganizer.displayIds)
+            .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+        // Create a mock for the target display area: second display
+        val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+            .thenReturn(secondDisplayArea)
+        whenever(transitions.startTransition(eq(TRANSIT_CHANGE), any(), anyOrNull()))
+            .thenReturn(transition)
+        val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = sourceDeskId)
+
+        controller.moveToNextDisplay(task1.taskId)
+
+        // Home / Wallpaper should be moved to front as the background of desktop tasks, otherwise
+        // fullscreen (non-desktop) tasks could remain visible.
+        val wct = getLatestTransition()
+        val homeReorderIndex = wct.indexOfReorder(homeTask, toTop = true)
+        val wallpaperReorderIndex = wct.indexOfReorder(wallpaperToken, toTop = true)
+        assertThat(homeReorderIndex).isNotEqualTo(-1)
+        assertThat(wallpaperReorderIndex).isNotEqualTo(-1)
+        // Wallpaper last, to be in front of Home.
+        assertThat(wallpaperReorderIndex).isGreaterThan(homeReorderIndex)
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun moveToNextDisplay_toDeskInOtherDisplay_movesToDeskAndActivates() {
         val transition = Binder()
@@ -2859,6 +2964,35 @@
     }
 
     @Test
+    fun moveToNextDisplay_movingToDesktop_sendsTaskbarRoundingUpdate() {
+        val transition = Binder()
+        val sourceDeskId = 1
+        val targetDeskId = 2
+        taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+        taskRepository.setDeskInactive(deskId = targetDeskId)
+        // Set up two display ids
+        whenever(rootTaskDisplayAreaOrganizer.displayIds)
+            .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+        // Create a mock for the target display area: second display
+        val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+            .thenReturn(secondDisplayArea)
+        whenever(transitions.startTransition(eq(TRANSIT_CHANGE), any(), anyOrNull()))
+            .thenReturn(transition)
+
+        val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = sourceDeskId)
+        taskRepository.addTaskToDesk(
+            displayId = DEFAULT_DISPLAY,
+            deskId = sourceDeskId,
+            taskId = task.taskId,
+            isVisible = true,
+        )
+        controller.moveToNextDisplay(task.taskId)
+
+        verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(anyBoolean())
+    }
+
+    @Test
     fun getTaskWindowingMode() {
         val fullscreenTask = setUpFullscreenTask()
         val freeformTask = setUpFreeformTask()
@@ -6452,6 +6586,25 @@
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
         Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
+    fun startLaunchTransition_desktopNotShowing_updatesDesktopEnterExitListener() {
+        setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+        taskRepository.setDeskInactive(deskId = 0)
+
+        controller.startLaunchTransition(
+            transitionType = TRANSIT_OPEN,
+            wct = WindowContainerTransaction(),
+            launchingTaskId = null,
+        )
+
+        verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(any())
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
     )
     fun startLaunchTransition_desktopShowing_doesNotReorderWallpaper() {
         val wct = WindowContainerTransaction()
@@ -6620,7 +6773,7 @@
         if (background) {
             whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
             whenever(recentTasksController.findTaskInBackground(task.taskId))
-                .thenReturn(createTaskInfo(task.taskId))
+                .thenReturn(createRecentTaskInfo(taskId = task.taskId, displayId = displayId))
         } else {
             whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
         }
@@ -6791,6 +6944,12 @@
         return arg.lastValue
     }
 
+    private fun getLatestTransition(): WindowContainerTransaction {
+        val arg = argumentCaptor<WindowContainerTransaction>()
+        verify(transitions).startTransition(any(), arg.capture(), anyOrNull())
+        return arg.lastValue
+    }
+
     private fun getLatestEnterDesktopWct(): WindowContainerTransaction {
         val arg = argumentCaptor<WindowContainerTransaction>()
         verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any())
@@ -7049,8 +7208,9 @@
     } ?: false
 }
 
-private fun createTaskInfo(id: Int) =
+private fun createRecentTaskInfo(taskId: Int, displayId: Int = DEFAULT_DISPLAY) =
     RecentTaskInfo().apply {
-        taskId = id
+        this.taskId = taskId
+        this.displayId = displayId
         token = WindowContainerToken(mock(IWindowContainerToken::class.java))
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/OWNERS
index 736d4cf..a7d1890 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/OWNERS
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/OWNERS
@@ -1,3 +1,2 @@
 # WM shell sub-module TV pip owners
-galinap@google.com
-bronger@google.com
\ No newline at end of file
+bronger@google.com
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index 414c014..ffef0d1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.splitscreen;
 
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
+
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
@@ -58,6 +60,7 @@
         doReturn(leash).when(out).getDividerLeash();
         doReturn(bounds1).when(out).getTopLeftBounds();
         doReturn(bounds2).when(out).getBottomRightBounds();
+        doReturn(SNAP_TO_2_50_50).when(out).calculateCurrentSnapPosition();
         return out;
     }
 
diff --git a/libs/hwui/OWNERS b/libs/hwui/OWNERS
index 70d13ab..9c06fd5 100644
--- a/libs/hwui/OWNERS
+++ b/libs/hwui/OWNERS
@@ -3,7 +3,7 @@
 alecmouri@google.com
 djsollen@google.com
 jreck@google.com
-njawad@google.com
+nscobie@google.com
 sumir@google.com
 
 # For text, e.g. Typeface, Font, Minikin, etc.
diff --git a/media/OWNERS b/media/OWNERS
index 5e39195..50995ea 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -18,5 +18,4 @@
 include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
 
 # SEA/KIR/BVE
-jtinker@google.com
 robertshih@google.com
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index b11a810..4e1d472 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -375,6 +375,9 @@
     /**
      * Extract DRM initialization data if it exists
      *
+     * <p>If the media contains a PSSH box, only PSSH version 0 is supported. The result for media
+     * with other PSSH versions is undefined.
+     *
      * @return DRM initialization data in the content, or {@code null}
      * if no recognizable DRM format is found;
      * @see DrmInitData
@@ -460,6 +463,10 @@
 
     /**
      * Get the PSSH info if present.
+     *
+     * <p>This method only supports version 0 PSSH boxes. The result for other versions is
+     * undefined.
+     *
      * @return a map of uuid-to-bytes, with the uuid specifying
      * the crypto scheme, and the bytes being the data specific to that scheme.
      * This can be {@code null} if the source does not contain PSSH info.
diff --git a/media/java/android/media/musicrecognition/OWNERS b/media/java/android/media/musicrecognition/OWNERS
index 037b048..820be00 100644
--- a/media/java/android/media/musicrecognition/OWNERS
+++ b/media/java/android/media/musicrecognition/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 830636
 
 oni@google.com
-volnov@google.com
 
diff --git a/media/java/android/media/projection/MediaProjectionConfig.java b/media/java/android/media/projection/MediaProjectionConfig.java
index 30f34fe..598b534 100644
--- a/media/java/android/media/projection/MediaProjectionConfig.java
+++ b/media/java/android/media/projection/MediaProjectionConfig.java
@@ -27,7 +27,6 @@
 import android.os.Parcelable;
 
 import com.android.internal.util.AnnotationValidations;
-import com.android.internal.util.DataClass;
 
 import java.lang.annotation.Retention;
 
@@ -35,36 +34,25 @@
  * Configure the {@link MediaProjection} session requested from
  * {@link MediaProjectionManager#createScreenCaptureIntent(MediaProjectionConfig)}.
  */
-@DataClass(
-        genEqualsHashCode = true,
-        genAidl = true,
-        genSetters = false,
-        genConstructor = false,
-        genBuilder = false,
-        genToString = false,
-        genHiddenConstDefs = true,
-        genHiddenGetters = true,
-        genConstDefs = false
-)
 public final class MediaProjectionConfig implements Parcelable {
 
     /**
      * The user, rather than the host app, determines which region of the display to capture.
+     *
      * @hide
      */
     public static final int CAPTURE_REGION_USER_CHOICE = 0;
 
     /**
      * The host app specifies a particular display to capture.
+     *
      * @hide
      */
     public static final int CAPTURE_REGION_FIXED_DISPLAY = 1;
 
     /** @hide */
-    @IntDef(prefix = "CAPTURE_REGION_", value = {
-            CAPTURE_REGION_USER_CHOICE,
-            CAPTURE_REGION_FIXED_DISPLAY
-    })
+    @IntDef(prefix = "CAPTURE_REGION_", value = {CAPTURE_REGION_USER_CHOICE,
+            CAPTURE_REGION_FIXED_DISPLAY})
     @Retention(SOURCE)
     public @interface CaptureRegion {
     }
@@ -72,7 +60,7 @@
     /**
      * The particular display to capture. Only used when {@link #getRegionToCapture()} is
      * {@link #CAPTURE_REGION_FIXED_DISPLAY}; ignored otherwise.
-     *
+     * <p>
      * Only supports values of {@link android.view.Display#DEFAULT_DISPLAY}.
      */
     @IntRange(from = DEFAULT_DISPLAY, to = DEFAULT_DISPLAY)
@@ -82,13 +70,7 @@
      * The region to capture. Defaults to the user's choice.
      */
     @CaptureRegion
-    private int mRegionToCapture = CAPTURE_REGION_USER_CHOICE;
-
-    /**
-     * Default instance, with region set to the user's choice.
-     */
-    private MediaProjectionConfig() {
-    }
+    private int mRegionToCapture;
 
     /**
      * Customized instance, with region set to the provided value.
@@ -129,51 +111,29 @@
      */
     @NonNull
     private static String captureRegionToString(int value) {
-        switch (value) {
-            case CAPTURE_REGION_USER_CHOICE:
-                return "CAPTURE_REGION_USERS_CHOICE";
-            case CAPTURE_REGION_FIXED_DISPLAY:
-                return "CAPTURE_REGION_GIVEN_DISPLAY";
-            default:
-                return Integer.toHexString(value);
-        }
+        return switch (value) {
+            case CAPTURE_REGION_USER_CHOICE -> "CAPTURE_REGION_USERS_CHOICE";
+            case CAPTURE_REGION_FIXED_DISPLAY -> "CAPTURE_REGION_GIVEN_DISPLAY";
+            default -> Integer.toHexString(value);
+        };
     }
 
     @Override
     public String toString() {
-        return "MediaProjectionConfig { "
-                + "displayToCapture = " + mDisplayToCapture + ", "
-                + "regionToCapture = " + captureRegionToString(mRegionToCapture)
-                + " }";
+        return "MediaProjectionConfig { " + "displayToCapture = " + mDisplayToCapture + ", "
+                + "regionToCapture = " + captureRegionToString(mRegionToCapture) + " }";
     }
 
 
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/media/java/android/media/projection/MediaProjectionConfig.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
     /**
      * The particular display to capture. Only used when {@link #getRegionToCapture()} is
      * {@link #CAPTURE_REGION_FIXED_DISPLAY}; ignored otherwise.
-     *
+     * <p>
      * Only supports values of {@link android.view.Display#DEFAULT_DISPLAY}.
      *
      * @hide
      */
-    @DataClass.Generated.Member
-    public @IntRange(from = DEFAULT_DISPLAY, to = DEFAULT_DISPLAY) int getDisplayToCapture() {
+    public int getDisplayToCapture() {
         return mDisplayToCapture;
     }
 
@@ -182,34 +142,21 @@
      *
      * @hide
      */
-    @DataClass.Generated.Member
     public @CaptureRegion int getRegionToCapture() {
         return mRegionToCapture;
     }
 
     @Override
-    @DataClass.Generated.Member
     public boolean equals(@Nullable Object o) {
-        // You can override field equality logic by defining either of the methods like:
-        // boolean fieldNameEquals(MediaProjectionConfig other) { ... }
-        // boolean fieldNameEquals(FieldType otherValue) { ... }
-
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
-        @SuppressWarnings("unchecked")
         MediaProjectionConfig that = (MediaProjectionConfig) o;
-        //noinspection PointlessBooleanExpression
-        return true
-                && mDisplayToCapture == that.mDisplayToCapture
+        return mDisplayToCapture == that.mDisplayToCapture
                 && mRegionToCapture == that.mRegionToCapture;
     }
 
     @Override
-    @DataClass.Generated.Member
     public int hashCode() {
-        // You can override field hashCode logic by defining methods like:
-        // int fieldNameHashCode() { ... }
-
         int _hash = 1;
         _hash = 31 * _hash + mDisplayToCapture;
         _hash = 31 * _hash + mRegionToCapture;
@@ -217,65 +164,36 @@
     }
 
     @Override
-    @DataClass.Generated.Member
     public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
         dest.writeInt(mDisplayToCapture);
         dest.writeInt(mRegionToCapture);
     }
 
     @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
+    public int describeContents() {
+        return 0;
+    }
 
     /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
     /* package-private */ MediaProjectionConfig(@NonNull android.os.Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
         int displayToCapture = in.readInt();
         int regionToCapture = in.readInt();
 
-        this.mDisplayToCapture = displayToCapture;
-        AnnotationValidations.validate(
-                IntRange.class, null, mDisplayToCapture,
-                "from", DEFAULT_DISPLAY,
-                "to", DEFAULT_DISPLAY);
-        this.mRegionToCapture = regionToCapture;
-        AnnotationValidations.validate(
-                CaptureRegion.class, null, mRegionToCapture);
-
-        // onConstructed(); // You can define this method to get a callback
+        mDisplayToCapture = displayToCapture;
+        mRegionToCapture = regionToCapture;
+        AnnotationValidations.validate(CaptureRegion.class, null, mRegionToCapture);
     }
 
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<MediaProjectionConfig> CREATOR
-            = new Parcelable.Creator<MediaProjectionConfig>() {
-        @Override
-        public MediaProjectionConfig[] newArray(int size) {
-            return new MediaProjectionConfig[size];
-        }
+    public static final @NonNull Parcelable.Creator<MediaProjectionConfig> CREATOR =
+            new Parcelable.Creator<>() {
+                @Override
+                public MediaProjectionConfig[] newArray(int size) {
+                    return new MediaProjectionConfig[size];
+                }
 
-        @Override
-        public MediaProjectionConfig createFromParcel(@NonNull android.os.Parcel in) {
-            return new MediaProjectionConfig(in);
-        }
-    };
-
-    @DataClass.Generated(
-            time = 1673548980960L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/media/java/android/media/projection/MediaProjectionConfig.java",
-            inputSignatures = "public static final  int CAPTURE_REGION_USER_CHOICE\npublic static final  int CAPTURE_REGION_FIXED_DISPLAY\nprivate @android.annotation.IntRange int mDisplayToCapture\nprivate @android.media.projection.MediaProjectionConfig.CaptureRegion int mRegionToCapture\npublic static @android.annotation.NonNull android.media.projection.MediaProjectionConfig createConfigForDefaultDisplay()\npublic static @android.annotation.NonNull android.media.projection.MediaProjectionConfig createConfigForUserChoice()\nprivate static @android.annotation.NonNull java.lang.String captureRegionToString(int)\npublic @java.lang.Override java.lang.String toString()\nclass MediaProjectionConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genSetters=false, genConstructor=false, genBuilder=false, genToString=false, genHiddenConstDefs=true, genHiddenGetters=true, genConstDefs=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
+                @Override
+                public MediaProjectionConfig createFromParcel(@NonNull android.os.Parcel in) {
+                    return new MediaProjectionConfig(in);
+                }
+            };
 }
diff --git a/media/jni/OWNERS b/media/jni/OWNERS
index fdddf13..84618a3 100644
--- a/media/jni/OWNERS
+++ b/media/jni/OWNERS
@@ -1,5 +1,5 @@
 # extra for MTP related files
-per-file android_mtp_*.cpp=aprasath@google.com,anothermark@google.com,kumarashishg@google.com,sarup@google.com,jsharkey@android.com,jameswei@google.com,rmojumder@google.com
+per-file android_mtp_*.cpp=aprasath@google.com,anothermark@google.com,sarup@google.com,jsharkey@android.com,jameswei@google.com,rmojumder@google.com
 
 # extra for TV related files
 per-file android_media_tv_*=hgchen@google.com,quxiangfang@google.com
diff --git a/opengl/java/android/opengl/OWNERS b/opengl/java/android/opengl/OWNERS
index e340bc6..4ec9e29 100644
--- a/opengl/java/android/opengl/OWNERS
+++ b/opengl/java/android/opengl/OWNERS
@@ -3,4 +3,3 @@
 sumir@google.com
 prahladk@google.com
 ianelliott@google.com
-lpy@google.com
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background.xml
index 9aa0bc3..0446873 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background.xml
@@ -19,16 +19,12 @@
     android:color="?android:colorControlHighlight">
     <item
         android:start="?android:attr/listPreferredItemPaddingStart"
-        android:end="?android:attr/listPreferredItemPaddingEnd"
-        android:top="2dp"
-        android:bottom="16dp">
+        android:end="?android:attr/listPreferredItemPaddingEnd">
         <shape android:shape="rectangle">
             <solid
                 android:color="@color/settingslib_materialColorSurfaceBright" />
             <corners
                 android:radius="@dimen/settingslib_preference_corner_radius" />
-            <padding
-                android:bottom="16dp"/>
         </shape>
     </item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom.xml
index 554cba5..25a936d 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom.xml
@@ -19,9 +19,7 @@
     android:color="?android:colorControlHighlight">
     <item
         android:start="?android:attr/listPreferredItemPaddingStart"
-        android:end="?android:attr/listPreferredItemPaddingEnd"
-        android:top="2dp"
-        android:bottom="16dp">
+        android:end="?android:attr/listPreferredItemPaddingEnd">
         <shape android:shape="rectangle">
             <solid
                 android:color="@color/settingslib_materialColorSurfaceBright" />
@@ -30,8 +28,6 @@
                 android:bottomLeftRadius="@dimen/settingslib_preference_corner_radius"
                 android:topRightRadius="4dp"
                 android:bottomRightRadius="@dimen/settingslib_preference_corner_radius" />
-            <padding
-                android:bottom="16dp"/>
         </shape>
     </item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_highlighted.xml
index c0c0869..db2800e 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_highlighted.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_highlighted.xml
@@ -19,8 +19,7 @@
     <item
         android:bottom="16dp"
         android:end="?android:attr/listPreferredItemPaddingEnd"
-        android:start="?android:attr/listPreferredItemPaddingStart"
-        android:top="2dp">
+        android:start="?android:attr/listPreferredItemPaddingStart">
         <shape
             android:shape="rectangle"
             android:tint="?android:attr/colorAccent">
@@ -29,8 +28,7 @@
                 android:bottomRightRadius="@dimen/settingslib_preference_corner_radius"
                 android:topLeftRadius="4dp"
                 android:topRightRadius="4dp" />
-            <padding android:bottom="16dp" />
             <solid android:color="#42000000" />
         </shape>
     </item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_selected.xml
index 543b237..98f95d92 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_selected.xml
@@ -19,9 +19,7 @@
     android:color="?android:colorControlHighlight">
     <item
         android:start="?android:attr/listPreferredItemPaddingStart"
-        android:end="?android:attr/listPreferredItemPaddingEnd"
-        android:top="2dp"
-        android:bottom="16dp">
+        android:end="?android:attr/listPreferredItemPaddingEnd">
         <shape android:shape="rectangle">
             <solid
                 android:color="@color/settingslib_materialColorSurfaceContainer" />
@@ -30,8 +28,6 @@
                 android:bottomLeftRadius="@dimen/settingslib_preference_corner_radius"
                 android:topRightRadius="4dp"
                 android:bottomRightRadius="@dimen/settingslib_preference_corner_radius" />
-            <padding
-                android:bottom="16dp"/>
         </shape>
     </item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center.xml
index b89a0dd..c4286fd 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center.xml
@@ -19,8 +19,7 @@
     android:color="?android:colorControlHighlight">
     <item
         android:start="?android:attr/listPreferredItemPaddingStart"
-        android:end="?android:attr/listPreferredItemPaddingEnd"
-        android:top="2dp">
+        android:end="?android:attr/listPreferredItemPaddingEnd">
         <shape android:shape="rectangle">
             <solid
                 android:color="@color/settingslib_materialColorSurfaceBright" />
@@ -28,4 +27,4 @@
                 android:radius="4dp" />
         </shape>
     </item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_highlighted.xml
index 8099d9b..194cdb0 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_highlighted.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_highlighted.xml
@@ -18,8 +18,7 @@
     android:color="?android:colorControlHighlight">
     <item
         android:end="?android:attr/listPreferredItemPaddingEnd"
-        android:start="?android:attr/listPreferredItemPaddingStart"
-        android:top="2dp">
+        android:start="?android:attr/listPreferredItemPaddingStart">
         <shape
             android:shape="rectangle"
             android:tint="?android:attr/colorAccent">
@@ -27,4 +26,4 @@
             <solid android:color="#42000000" />
         </shape>
     </item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_selected.xml
index 6d2cd1a..8bc2f2f 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_selected.xml
@@ -19,8 +19,7 @@
     android:color="?android:colorControlHighlight">
     <item
         android:start="?android:attr/listPreferredItemPaddingStart"
-        android:end="?android:attr/listPreferredItemPaddingEnd"
-        android:top="2dp">
+        android:end="?android:attr/listPreferredItemPaddingEnd">
         <shape android:shape="rectangle">
             <solid
                 android:color="@color/settingslib_materialColorSurfaceContainer" />
@@ -28,4 +27,4 @@
                 android:radius="4dp" />
         </shape>
     </item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_highlighted.xml
index a119a4a..2341661 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_highlighted.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_highlighted.xml
@@ -19,14 +19,12 @@
     <item
         android:bottom="16dp"
         android:end="?android:attr/listPreferredItemPaddingEnd"
-        android:start="?android:attr/listPreferredItemPaddingStart"
-        android:top="2dp">
+        android:start="?android:attr/listPreferredItemPaddingStart">
         <shape
             android:shape="rectangle"
             android:tint="?android:attr/colorAccent">
             <corners android:radius="@dimen/settingslib_preference_corner_radius" />
-            <padding android:bottom="16dp" />
             <solid android:color="#42000000" />
         </shape>
     </item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_selected.xml
index bcdbf1d..99704f2d 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_selected.xml
@@ -19,16 +19,12 @@
     android:color="?android:colorControlHighlight">
     <item
         android:start="?android:attr/listPreferredItemPaddingStart"
-        android:end="?android:attr/listPreferredItemPaddingEnd"
-        android:top="2dp"
-        android:bottom="16dp">
+        android:end="?android:attr/listPreferredItemPaddingEnd">
         <shape android:shape="rectangle">
             <solid
                 android:color="@color/settingslib_materialColorSurfaceContainer" />
             <corners
                 android:radius="@dimen/settingslib_preference_corner_radius" />
-            <padding
-                android:bottom="16dp"/>
         </shape>
     </item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top.xml
index 7955e44..3a59386 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top.xml
@@ -19,8 +19,7 @@
     android:color="?android:colorControlHighlight">
     <item
         android:start="?android:attr/listPreferredItemPaddingStart"
-        android:end="?android:attr/listPreferredItemPaddingEnd"
-        android:top="2dp">
+        android:end="?android:attr/listPreferredItemPaddingEnd">
         <shape android:shape="rectangle">
             <solid
                 android:color="@color/settingslib_materialColorSurfaceBright" />
@@ -31,4 +30,4 @@
                 android:bottomRightRadius="4dp" />
         </shape>
     </item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_highlighted.xml
index 052eb01..edace29 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_highlighted.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_highlighted.xml
@@ -19,8 +19,7 @@
     <item
         android:color="?android:attr/colorAccent"
         android:end="?android:attr/listPreferredItemPaddingEnd"
-        android:start="?android:attr/listPreferredItemPaddingStart"
-        android:top="2dp">
+        android:start="?android:attr/listPreferredItemPaddingStart">
         <shape
             android:shape="rectangle"
             android:tint="?android:attr/colorAccent">
@@ -32,4 +31,4 @@
             <solid android:color="#42000000" />
         </shape>
     </item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_selected.xml
index d4b658c..b2d6d9d 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_selected.xml
@@ -19,8 +19,7 @@
     android:color="?android:colorControlHighlight">
     <item
         android:start="?android:attr/listPreferredItemPaddingStart"
-        android:end="?android:attr/listPreferredItemPaddingEnd"
-        android:top="2dp">
+        android:end="?android:attr/listPreferredItemPaddingEnd">
         <shape android:shape="rectangle">
             <solid
                 android:color="@color/settingslib_materialColorSurfaceContainer" />
@@ -31,4 +30,4 @@
                 android:bottomRightRadius="4dp" />
         </shape>
     </item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
index 3ccbbc0..2d6b6cf 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
@@ -28,6 +28,7 @@
         <item name="switchStyle">@style/SwitchCompat.SettingsLib</item>
         <item name="android:progressBarStyleHorizontal">@style/HorizontalProgressBar.SettingsLib</item>
         <item name="android:listDivider">@drawable/settingslib_list_divider</item>
+        <item name="android:colorBackground">@color/settingslib_materialColorSurfaceContainerLowest</item>
     </style>
 
     <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v31" />
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
index 8d12f01..22cd873 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.widget
 
+import android.graphics.Rect
 import android.os.Bundle
 import android.view.LayoutInflater;
 import android.view.View
@@ -24,6 +25,7 @@
 import androidx.preference.PreferenceFragmentCompat
 import androidx.preference.PreferenceScreen
 import androidx.recyclerview.widget.RecyclerView
+import com.android.settingslib.widget.theme.R
 
 /** Base class for Settings to use PreferenceFragmentCompat */
 abstract class SettingsBasePreferenceFragment : PreferenceFragmentCompat() {
@@ -43,6 +45,7 @@
         if (SettingsThemeHelper.isExpressiveTheme(requireContext())) {
             // Don't allow any divider in between the preferences in expressive design.
             setDivider(null)
+            this.listView.addItemDecoration(MarginItemDecoration())
         }
     }
 
@@ -51,4 +54,18 @@
             return SettingsPreferenceGroupAdapter(preferenceScreen)
         return super.onCreateAdapter(preferenceScreen)
     }
+
+    internal class MarginItemDecoration() : RecyclerView.ItemDecoration() {
+        override fun getItemOffsets(
+            outRect: Rect,
+            view: View,
+            parent: RecyclerView,
+            state: RecyclerView.State,
+        ) {
+            with(outRect) {
+                bottom =
+                    view.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_radius_extrasmall1)
+            }
+        }
+  }
 }
diff --git a/packages/SettingsLib/Spa/OWNERS b/packages/SettingsLib/Spa/OWNERS
index 67386d1..d2941de 100644
--- a/packages/SettingsLib/Spa/OWNERS
+++ b/packages/SettingsLib/Spa/OWNERS
@@ -1,9 +1,6 @@
 include platform/frameworks/base:/packages/SettingsLib/OWNERS
 
+zhibinliu@google.com
 chaohuiw@google.com
-hanxu@google.com
-kellyz@google.com
 pierreqian@google.com
-lijun@google.com
-songchenxi@google.com
-cyl@google.com
+lijun@google.com
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_actionButtons.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_actionButtons.png
index b2f3cf1..65418b9 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_actionButtons.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_actionButtons.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.png
index 75c8e6e..c9091fb 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.png
index 06f0059..cdc1e79 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_actionButtons.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_actionButtons.png
index fa06927..614fbe9 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_actionButtons.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_actionButtons.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_imageIllustration.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_imageIllustration.png
index a5f3fa5..ce1257f 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_imageIllustration.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_imageIllustration.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.png
index b72c8db..6b1233c 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png
index 928e926..782957d 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.png
index 06f0059..cdc1e79 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_actionButtons.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_actionButtons.png
index 63983ee..427f202 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_actionButtons.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_actionButtons.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_barChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_barChart.png
index 8fcc350..e372f7b 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_barChart.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_barChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.png
index c2f6165..1ed1efc 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_imageIllustration.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_imageIllustration.png
index f32d7421..6283dd1 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_imageIllustration.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_imageIllustration.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_lineChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_lineChart.png
index 6659d7c..a4eb19e 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_lineChart.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_lineChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.png
index 15c86dc..bd1bb28 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_pieChart.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_pieChart.png
index 7b6e702..d0d5ac1 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_pieChart.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_pieChart.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.png
index cd44fb8..5cf7381 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.png
index d31f2c4..96f614c 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png
index 8a2b800..3cbe715 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_spinner.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_spinner.png
index 5be3a21..f151798 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_spinner.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_spinner.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.png
index 9aee004..218e8e3 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.png
index cc74aac..44b32e2 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/TimeMeasurer.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/TimeMeasurer.kt
index b23f4e0..ef2745c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/TimeMeasurer.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/TimeMeasurer.kt
@@ -14,14 +14,11 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalTime::class)
-
 package com.android.settingslib.spa.framework.compose
 
 import android.util.Log
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
-import kotlin.time.ExperimentalTime
 import kotlin.time.TimeSource
 
 const val ENABLE_MEASURE_TIME = false
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
index b1bb79d..068ff06 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
@@ -87,15 +87,28 @@
 
 @Composable
 internal fun BaseIcon(icon: @Composable (() -> Unit)?, modifier: Modifier, paddingStart: Dp) {
-    if (icon != null) {
-        Box(
-            modifier = modifier.size(SettingsDimension.itemIconContainerSize),
-            contentAlignment = Alignment.Center,
-        ) {
-            icon()
+    if (isSpaExpressiveEnabled) {
+        Spacer(modifier = Modifier.width(width = paddingStart))
+        if (icon != null) {
+            Box(
+                modifier = modifier.size(SettingsDimension.itemIconContainerSizeSmall),
+                contentAlignment = Alignment.Center,
+            ) {
+                icon()
+            }
+            Spacer(modifier = Modifier.width(width = SettingsDimension.paddingExtraSmall6))
         }
     } else {
-        Spacer(modifier = Modifier.width(width = paddingStart))
+        if (icon != null) {
+            Box(
+                modifier = modifier.size(SettingsDimension.itemIconContainerSize),
+                contentAlignment = Alignment.Center,
+            ) {
+                icon()
+            }
+        } else {
+            Spacer(modifier = Modifier.width(width = paddingStart))
+        }
     }
 }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
index b9449ac..50bfe8c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
@@ -1,6 +1,5 @@
 # Default reviewers for this and subdirectories.
 andychou@google.com
-arcwang@google.com
 asapperstein@google.com
 changbetty@google.com
 qal@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index c98a741..99c4e21c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -456,11 +456,16 @@
     @GuardedBy("mLock")
     private void loadAconfigDefaultValuesLocked(List<String> filePaths) {
         for (String fileName : filePaths) {
-            try (FileInputStream inputStream = new FileInputStream(fileName)) {
-                loadAconfigDefaultValues(
-                        inputStream.readAllBytes(), mNamespaceDefaults, mAconfigDefaultFlags);
-            } catch (IOException e) {
-                Slog.e(LOG_TAG, "failed to read protobuf", e);
+            File f = new File(fileName);
+            if (f.isFile() && f.canRead()) {
+                try (FileInputStream inputStream = new FileInputStream(fileName)) {
+                    loadAconfigDefaultValues(
+                            inputStream.readAllBytes(), mNamespaceDefaults, mAconfigDefaultFlags);
+                } catch (IOException e) {
+                    Slog.e(LOG_TAG, "failed to read protobuf", e);
+                }
+            } else {
+                Slog.d(LOG_TAG, "No protobuf file at " + fileName);
             }
         }
     }
diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS
index 576afdc..2fa707e 100644
--- a/packages/Shell/OWNERS
+++ b/packages/Shell/OWNERS
@@ -7,9 +7,7 @@
 svetoslavganov@google.com
 hackbod@google.com
 yamasani@google.com
-toddke@google.com
 patb@google.com
-cbrubaker@google.com
 omakoto@google.com
 michaelwr@google.com
 ronish@google.com
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index f2c76ba..7e8d549 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -203,6 +203,16 @@
 }
 
 flag {
+    name: "notifications_hun_shared_animation_values"
+    namespace: "systemui"
+    description: "Adds a shared class for fetching HUN animation values."
+    bug: "393369891"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "notification_undo_guts_on_config_changed"
     namespace: "systemui"
     description: "Fixes a bug where a theme or font change while notification guts were open"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ComposableControllerFactory.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ComposableControllerFactory.kt
new file mode 100644
index 0000000..c842159
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ComposableControllerFactory.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.animation
+
+import android.content.ComponentName
+import android.util.Log
+import com.android.systemui.animation.ActivityTransitionAnimator.Controller
+import com.android.systemui.animation.ActivityTransitionAnimator.ControllerFactory
+import kotlinx.coroutines.flow.MutableStateFlow
+
+private const val TAG = "ComposableControllerFactory"
+
+/**
+ * [ControllerFactory] extension for Compose. Since composables are not guaranteed to be part of the
+ * composition when [ControllerFactory.createController] is called, this class provides a way for
+ * the composable to register itself at the time of composition, and deregister itself when
+ * disposed.
+ */
+abstract class ComposableControllerFactory(
+    cookie: ActivityTransitionAnimator.TransitionCookie,
+    component: ComponentName?,
+    launchCujType: Int? = null,
+    returnCujType: Int? = null,
+) : ControllerFactory(cookie, component, launchCujType, returnCujType) {
+    /**
+     * The object to be used to create [Controller]s, when its associate composable is in the
+     * composition.
+     */
+    protected val expandable = MutableStateFlow<Expandable?>(null)
+
+    /** To be called when the composable to be animated enters composition. */
+    fun onCompose(expandable: Expandable) {
+        if (TransitionAnimator.DEBUG) {
+            Log.d(TAG, "Composable entered composition (expandable=$expandable")
+        }
+        this.expandable.value = expandable
+    }
+
+    /** To be called when the composable to be animated exits composition. */
+    fun onDispose() {
+        if (TransitionAnimator.DEBUG) {
+            Log.d(TAG, "Composable left composition (expandable=${this.expandable.value}")
+        }
+        this.expandable.value = null
+    }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index 1e3c4c9..a352b1e 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -84,6 +84,7 @@
 import androidx.savedstate.setViewTreeSavedStateRegistryOwner
 import com.android.compose.modifiers.thenIf
 import com.android.compose.ui.graphics.FullScreenComposeViewInOverlay
+import com.android.systemui.animation.ComposableControllerFactory
 import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.TransitionAnimator
 import kotlin.math.max
@@ -119,6 +120,10 @@
  *    }
  * ```
  *
+ * [transitionControllerFactory] must be defined when this [Expandable] is registered for a
+ * long-term launch or return animation, to ensure that animation controllers can be created
+ * correctly.
+ *
  * @sample com.android.systemui.compose.gallery.ActivityLaunchScreen
  * @sample com.android.systemui.compose.gallery.DialogLaunchScreen
  */
@@ -134,10 +139,17 @@
     // TODO(b/285250939): Default this to true then remove once the Compose QS expandables have
     // proven that the new implementation is robust.
     useModifierBasedImplementation: Boolean = false,
+    transitionControllerFactory: ComposableControllerFactory? = null,
     content: @Composable (Expandable) -> Unit,
 ) {
     Expandable(
-        rememberExpandableController(color, shape, contentColor, borderStroke),
+        rememberExpandableController(
+            color,
+            shape,
+            contentColor,
+            borderStroke,
+            transitionControllerFactory,
+        ),
         modifier,
         onClick,
         interactionSource,
@@ -183,6 +195,17 @@
 ) {
     val controller = controller as ExpandableControllerImpl
 
+    if (controller.transitionControllerFactory != null) {
+        DisposableEffect(controller.transitionControllerFactory) {
+            // Notify the transition controller factory that the expandable is now available, so it
+            // can move forward with any pending requests.
+            controller.transitionControllerFactory.onCompose(controller.expandable)
+            // Once this composable is gone, the transition controller factory must be notified so
+            // it doesn't accepts requests providing stale content.
+            onDispose { controller.transitionControllerFactory.onDispose() }
+        }
+    }
+
     if (useModifierBasedImplementation) {
         Box(modifier.expandable(controller, onClick, interactionSource)) {
             WrappedContent(controller.expandable, controller.contentColor, content)
@@ -308,34 +331,28 @@
     interactionSource: MutableInteractionSource? = null,
 ): Modifier {
     val controller = controller as ExpandableControllerImpl
+    val graphicsLayer = rememberGraphicsLayer()
 
     val isAnimating = controller.isAnimating
-    val drawInOverlayModifier =
-        if (isAnimating) {
-            val graphicsLayer = rememberGraphicsLayer()
-
-            FullScreenComposeViewInOverlay(controller.overlay) { view ->
-                Modifier.then(DrawExpandableInOverlayElement(view, controller, graphicsLayer))
-            }
-
-            Modifier.drawWithContent { graphicsLayer.record { this@drawWithContent.drawContent() } }
-        } else {
-            null
+    if (isAnimating) {
+        FullScreenComposeViewInOverlay(controller.overlay) { view ->
+            Modifier.then(DrawExpandableInOverlayElement(view, controller, graphicsLayer))
         }
+    }
 
+    val drawContent = !isAnimating && !controller.isDialogShowing
     return this.thenIf(onClick != null) { Modifier.minimumInteractiveComponentSize() }
-        .thenIf(!isAnimating) {
+        .thenIf(drawContent) {
             Modifier.border(controller)
                 .then(clickModifier(controller, onClick, interactionSource))
                 .background(controller.color, controller.shape)
         }
-        .thenIf(drawInOverlayModifier != null) { drawInOverlayModifier!! }
         .onPlaced { controller.boundsInComposeViewRoot = it.boundsInRoot() }
-        .thenIf(!isAnimating && controller.isDialogShowing) {
-            Modifier.layout { measurable, constraints ->
-                measurable.measure(constraints).run {
-                    layout(width, height) { /* Do not place/draw. */ }
-                }
+        .drawWithContent {
+            graphicsLayer.record { this@drawWithContent.drawContent() }
+
+            if (drawContent) {
+                drawLayer(graphicsLayer)
             }
         }
 }
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index a03c896..72da175e 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -47,6 +47,7 @@
 import androidx.compose.ui.unit.LayoutDirection
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.ComposableControllerFactory
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
@@ -77,6 +78,7 @@
     shape: Shape,
     contentColor: Color = contentColorFor(color),
     borderStroke: BorderStroke? = null,
+    transitionControllerFactory: ComposableControllerFactory? = null,
 ): ExpandableController {
     val composeViewRoot = LocalView.current
     val density = LocalDensity.current
@@ -95,6 +97,7 @@
             composeViewRoot,
             density,
             layoutDirection,
+            transitionControllerFactory,
         ) {
             ExpandableControllerImpl(
                 color,
@@ -103,6 +106,7 @@
                 borderStroke,
                 composeViewRoot,
                 density,
+                transitionControllerFactory,
                 layoutDirection,
                 { isComposed },
             )
@@ -127,6 +131,7 @@
     internal val borderStroke: BorderStroke?,
     internal val composeViewRoot: View,
     internal val density: Density,
+    internal val transitionControllerFactory: ComposableControllerFactory?,
     private val layoutDirection: LayoutDirection,
     private val isComposed: () -> Boolean,
 ) : ExpandableController {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
index f905527..b9200c1 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
@@ -55,7 +55,6 @@
         val layerCfg =
             LayerConfig(
                 style = FontTextStyle(lineHeight = 147.25f),
-                timespec = DigitalTimespec.DIGIT_PAIR,
                 alignment = DigitalAlignment(HorizontalAlignment.CENTER, VerticalAlignment.CENTER),
                 aodStyle =
                     FontTextStyle(
@@ -63,12 +62,23 @@
                         transitionDuration = 750,
                     ),
 
-                // Placeholder
+                // Placeholders
+                timespec = DigitalTimespec.TIME_FULL_FORMAT,
                 dateTimeFormat = "hh:mm",
             )
 
-        createController(layerCfg.copy(dateTimeFormat = "hh"))
-        createController(layerCfg.copy(dateTimeFormat = "mm"))
+        createController(
+            layerCfg.copy(timespec = DigitalTimespec.FIRST_DIGIT, dateTimeFormat = "hh")
+        )
+        createController(
+            layerCfg.copy(timespec = DigitalTimespec.SECOND_DIGIT, dateTimeFormat = "hh")
+        )
+        createController(
+            layerCfg.copy(timespec = DigitalTimespec.FIRST_DIGIT, dateTimeFormat = "mm")
+        )
+        createController(
+            layerCfg.copy(timespec = DigitalTimespec.SECOND_DIGIT, dateTimeFormat = "mm")
+        )
     }
 
     private fun refreshTime() {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index ec99af1..2d0ca53 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -124,8 +124,10 @@
             maxChildSize.y * AOD_VERTICAL_TRANSLATE_RATIO
         )
         */
+
+        val xScale = if (childViews.size < 4) 1f else 2f
         val yBuffer = context.resources.getDimensionPixelSize(R.dimen.clock_vertical_digit_buffer)
-        return (maxChildSize + aodTranslate.abs()) * VPointF(1f, 2f) + VPointF(0f, yBuffer)
+        return (maxChildSize + aodTranslate.abs()) * VPointF(xScale, 2f) + VPointF(0f, yBuffer)
     }
 
     override fun onViewAdded(child: View?) {
@@ -226,19 +228,22 @@
                     when (child.id) {
                         R.id.HOUR_FIRST_DIGIT -> VPointF.ZERO
                         R.id.HOUR_SECOND_DIGIT -> VPointF(x, 0f)
-                        R.id.MINUTE_FIRST_DIGIT -> VPointF(0f, y + yBuffer)
-                        R.id.MINUTE_SECOND_DIGIT -> this
                         R.id.HOUR_DIGIT_PAIR -> VPointF.ZERO
-                        // Add a small vertical buffer for the second digit pair
+                        // Add a small vertical buffer for second line views
                         R.id.MINUTE_DIGIT_PAIR -> VPointF(0f, y + yBuffer)
+                        R.id.MINUTE_FIRST_DIGIT -> VPointF(0f, y + yBuffer)
+                        R.id.MINUTE_SECOND_DIGIT -> VPointF(x, y + yBuffer)
                         else -> VPointF.ZERO
                     }
                 }
 
             val childSize = child.measuredSize
-            offset += VPointF((measuredWidth - childSize.x) / 2f, 0f)
             offset += aodTranslate.abs()
 
+            // Horizontal offset to center each view in the available space
+            val midX = if (childViews.size < 4) measuredWidth / 2f else measuredWidth / 4f
+            offset += VPointF(midX - childSize.x / 2f, 0f)
+
             val setPos = if (isLayout) child::layout else child::setLeftTopRightBottom
             setPos(
                 offset.x.roundToInt(),
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index 479e146..015a827 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -425,12 +425,10 @@
         return VPointF(
             when {
                 mode.x == EXACTLY -> MeasureSpec.getSize(widthMeasureSpec).toFloat()
-                isSingleDigit() -> maxSingleDigitWidth
                 else -> interpBounds.width() + 2 * lockScreenPaint.strokeWidth
             },
             when {
                 mode.y == EXACTLY -> MeasureSpec.getSize(heightMeasureSpec).toFloat()
-                isSingleDigit() -> maxSingleDigitHeight
                 else -> interpBounds.height() + 2 * lockScreenPaint.strokeWidth
             },
         )
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 88c9e74..0cfb36d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -36,6 +36,7 @@
 import android.view.View
 import android.view.WindowInsets
 import android.view.WindowManager
+import android.view.accessibility.AccessibilityManager
 import android.widget.ScrollView
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -114,6 +115,7 @@
     @Mock lateinit var selectedUserInteractor: SelectedUserInteractor
     @Mock private lateinit var packageManager: PackageManager
     @Mock private lateinit var activityTaskManager: ActivityTaskManager
+    @Mock private lateinit var accessibilityManager: AccessibilityManager
     @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
 
     private lateinit var displayRepository: FakeDisplayRepository
@@ -678,6 +680,7 @@
                 udfpsUtils,
                 iconProvider,
                 activityTaskManager,
+                accessibilityManager,
             ),
             { credentialViewModel },
             fakeExecutor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index dfea784..197b0ee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -23,15 +23,16 @@
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.Display.TYPE_EXTERNAL
 import android.view.Display.TYPE_INTERNAL
+import android.view.IWindowManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.FlowValue
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.util.mockito.kotlinArgumentCaptor
 import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.utils.os.FakeHandler
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.TestScope
@@ -46,6 +47,7 @@
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
@@ -53,7 +55,11 @@
 class DisplayRepositoryTest : SysuiTestCase() {
 
     private val displayManager = mock<DisplayManager>()
+    private val commandQueue = mock<CommandQueue>()
+    private val windowManager = mock<IWindowManager>()
+
     private val displayListener = kotlinArgumentCaptor<DisplayManager.DisplayListener>()
+    private val commandQueueCallbacks = kotlinArgumentCaptor<CommandQueue.Callbacks>()
     private val connectedDisplayListener = kotlinArgumentCaptor<DisplayManager.DisplayListener>()
 
     private val testHandler = FakeHandler(Looper.getMainLooper())
@@ -67,6 +73,8 @@
     private val displayRepository: DisplayRepositoryImpl by lazy {
         DisplayRepositoryImpl(
                 displayManager,
+                commandQueue,
+                windowManager,
                 testHandler,
                 TestScope(UnconfinedTestDispatcher()),
                 UnconfinedTestDispatcher(),
@@ -513,6 +521,115 @@
             assertThat(displayRepository.getDisplay(2)).isNull()
         }
 
+    @Test
+    fun displayIdsWithSystemDecorations_onStart_emitsDisplaysWithSystemDecorations() =
+        testScope.runTest {
+            setDisplays(0, 1, 2)
+            whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true)
+            whenever(windowManager.shouldShowSystemDecors(1)).thenReturn(false)
+            whenever(windowManager.shouldShowSystemDecors(2)).thenReturn(true)
+
+            val displayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+            assertThat(displayIdsWithSystemDecorations).containsExactly(0, 2)
+        }
+
+    @Test
+    fun displayIdsWithSystemDecorations_systemDecorationAdded_emitsIncludingNewDisplayIds() =
+        testScope.runTest {
+            setDisplays(0)
+            whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true)
+            val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+            sendOnDisplayAddSystemDecorations(2)
+            sendOnDisplayAddSystemDecorations(3)
+
+            assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(0, 2, 3)
+        }
+
+    @Test
+    fun displayIdsWithSystemDecorations_systemDecorationAdded_emitsToNewSubscribers() =
+        testScope.runTest {
+            setDisplays(0)
+            whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true)
+
+            val priorDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+            sendOnDisplayAddSystemDecorations(1)
+            assertThat(priorDisplayIdsWithSystemDecorations).containsExactly(0, 1)
+
+            val lastDisplayIdsWithSystemDecorations by
+                collectLastValue(displayRepository.displayIdsWithSystemDecorations)
+            assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(0, 1)
+        }
+
+    @Test
+    fun displayIdsWithSystemDecorations_systemDecorationRemoved_doesNotEmitRemovedDisplayId() =
+        testScope.runTest {
+            val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+            sendOnDisplayAddSystemDecorations(1)
+            sendOnDisplayAddSystemDecorations(2)
+            sendOnDisplayRemoveSystemDecorations(2)
+
+            assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1)
+        }
+
+    @Test
+    fun displayIdsWithSystemDecorations_systemDecorationsRemoved_nonExistentDisplay_noEffect() =
+        testScope.runTest {
+            val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+            sendOnDisplayAddSystemDecorations(1)
+            sendOnDisplayRemoveSystemDecorations(2)
+
+            assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1)
+        }
+
+    @Test
+    fun displayIdsWithSystemDecorations_displayRemoved_doesNotEmitRemovedDisplayId() =
+        testScope.runTest {
+            val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+            sendOnDisplayAddSystemDecorations(1)
+            sendOnDisplayAddSystemDecorations(2)
+            sendOnDisplayRemoved(2)
+
+            assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1)
+        }
+
+    @Test
+    fun displayIdsWithSystemDecorations_displayRemoved_nonExistentDisplay_noEffect() =
+        testScope.runTest {
+            val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+            sendOnDisplayAddSystemDecorations(1)
+            sendOnDisplayRemoved(2)
+
+            assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1)
+        }
+
+    @Test
+    fun displayIdsWithSystemDecorations_onFlowCollection_commandQueueCallbackRegistered() =
+        testScope.runTest {
+            val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+            assertThat(lastDisplayIdsWithSystemDecorations).isEmpty()
+
+            verify(commandQueue, times(1)).addCallback(any())
+        }
+
+    @Test
+    fun displayIdsWithSystemDecorations_afterFlowCollection_commandQueueCallbackUnregistered() {
+        testScope.runTest {
+            val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+            assertThat(lastDisplayIdsWithSystemDecorations).isEmpty()
+
+            verify(commandQueue, times(1)).addCallback(any())
+        }
+        verify(commandQueue, times(1)).removeCallback(any())
+    }
+
     private fun Iterable<Display>.ids(): List<Int> = map { it.displayId }
 
     private fun Iterable<Set<Display>>.toIdSets(): List<Set<Int>> = map { it.ids().toSet() }
@@ -550,6 +667,14 @@
         return flowValue
     }
 
+    // Wrapper to capture the displayListener and commandQueueCallbacks.
+    private fun TestScope.latestDisplayIdsWithSystemDecorationsValue(): FlowValue<Set<Int>?> {
+        val flowValue = collectLastValue(displayRepository.displayIdsWithSystemDecorations)
+        captureAddedRemovedListener()
+        captureCommandQueueCallbacks()
+        return flowValue
+    }
+
     private fun captureAddedRemovedListener() {
         verify(displayManager)
             .registerDisplayListener(
@@ -563,6 +688,10 @@
             )
     }
 
+    private fun captureCommandQueueCallbacks() {
+        verify(commandQueue).addCallback(commandQueueCallbacks.capture())
+    }
+
     private fun sendOnDisplayAdded(id: Int, displayType: Int) {
         val mockDisplay = display(id = id, type = displayType)
         whenever(displayManager.getDisplay(eq(id))).thenReturn(mockDisplay)
@@ -592,6 +721,14 @@
         connectedDisplayListener.value.onDisplayChanged(id)
     }
 
+    private fun sendOnDisplayRemoveSystemDecorations(id: Int) {
+        commandQueueCallbacks.value.onDisplayRemoveSystemDecorations(id)
+    }
+
+    private fun sendOnDisplayAddSystemDecorations(id: Int) {
+        commandQueueCallbacks.value.onDisplayAddSystemDecorations(id)
+    }
+
     private fun setDisplays(displays: List<Display>) {
         whenever(displayManager.displays).thenReturn(displays.toTypedArray())
         displays.forEach { display ->
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index 8058eca..1665895 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -130,7 +130,7 @@
     }
 
     @Test
-    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testApplyDefaultConstraints_LargeClock_SplitShade() =
         kosmos.testScope.runTest {
             with(kosmos) {
@@ -147,7 +147,7 @@
         }
 
     @Test
-    @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testApplyDefaultConstraints_LargeClock_SplitShade_ReactiveVariantsOn() =
         kosmos.testScope.runTest {
             with(kosmos) {
@@ -165,7 +165,7 @@
         }
 
     @Test
-    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testApplyDefaultConstraints_LargeClock_NonSplitShade() =
         kosmos.testScope.runTest {
             with(kosmos) {
@@ -189,7 +189,7 @@
         }
 
     @Test
-    @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testApplyDefaultConstraints_LargeClock_NonSplitShade_reactiveVariantsOn() =
         kosmos.testScope.runTest {
             with(kosmos) {
@@ -262,7 +262,7 @@
         }
 
     @Test
-    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testApplyDefaultConstraints_SmallClock_SplitShade() =
         kosmos.testScope.runTest {
             with(kosmos) {
@@ -286,7 +286,7 @@
         }
 
     @Test
-    @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testApplyDefaultConstraints_SmallClock_SplitShade_ReactiveVariantsOn() =
         kosmos.testScope.runTest {
             with(kosmos) {
@@ -311,7 +311,7 @@
         }
 
     @Test
-    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testApplyDefaultConstraints_SmallClock_NonSplitShade() =
         kosmos.testScope.runTest {
             with(kosmos) {
@@ -334,7 +334,7 @@
         }
 
     @Test
-    @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testApplyDefaultConstraints_SmallClock_NonSplitShade_ReactiveVariantsOn() =
         kosmos.testScope.runTest {
             with(kosmos) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
index f9bed23..374bcbf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
@@ -50,7 +50,7 @@
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
-@DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+@DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
 class SmartspaceSectionTest : SysuiTestCase() {
     private lateinit var underTest: SmartspaceSection
     @Mock private lateinit var keyguardClockViewModel: KeyguardClockViewModel
@@ -102,7 +102,7 @@
     }
 
     @Test
-    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testAddViews_notSmartspaceEnabled() {
         whenever(keyguardSmartspaceViewModel.isSmartspaceEnabled).thenReturn(false)
         val constraintLayout = ConstraintLayout(mContext)
@@ -113,7 +113,7 @@
     }
 
     @Test
-    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testAddViews_smartspaceEnabled_dateWeatherDecoupled() {
         whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(true)
         underTest.addViews(constraintLayout)
@@ -132,7 +132,7 @@
     }
 
     @Test
-    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testConstraintsWhenShadeLayoutIsNotWide() {
         underTest.addViews(constraintLayout)
         underTest.applyConstraints(constraintSet)
@@ -142,7 +142,7 @@
     }
 
     @Test
-    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testConstraintsWhenShadeLayoutIsWide() {
         isShadeLayoutWide.value = true
 
@@ -154,7 +154,7 @@
     }
 
     @Test
-    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testConstraintsWhenNotHasCustomWeatherDataDisplay() {
         whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(true)
         underTest.addViews(constraintLayout)
@@ -169,7 +169,7 @@
     }
 
     @Test
-    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testConstraintsWhenHasCustomWeatherDataDisplay() {
         hasCustomWeatherDataDisplay.value = true
         underTest.addViews(constraintLayout)
@@ -180,7 +180,7 @@
     }
 
     @Test
-    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testNormalDateWeatherVisibility() {
         isWeatherVisibleFlow.value = true
         underTest.addViews(constraintLayout)
@@ -194,7 +194,7 @@
     }
 
     @Test
-    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+    @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
     fun testCustomDateWeatherVisibility() {
         hasCustomWeatherDataDisplay.value = true
         underTest.addViews(constraintLayout)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
index 4e14fec..c71b107 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
@@ -61,11 +61,11 @@
     fun setUp() {
         context.orCreateTestableResources.addOverride(
             R.dimen.qs_media_enabled_seekbar_height,
-            enabledHeight
+            enabledHeight,
         )
         context.orCreateTestableResources.addOverride(
             R.dimen.qs_media_disabled_seekbar_height,
-            disabledHeight
+            disabledHeight,
         )
 
         seekBarView = SeekBar(context)
@@ -116,9 +116,6 @@
         // THEN seek bar shows the progress
         assertThat(seekBarView.progress).isEqualTo(3000)
         assertThat(seekBarView.max).isEqualTo(120000)
-
-        val desc = context.getString(R.string.controls_media_seekbar_description, "00:03", "02:00")
-        assertThat(seekBarView.contentDescription).isEqualTo(desc)
     }
 
     @Test
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
index 67d0ade..0caddf4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/ConversationNotificationProcessorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/ConversationNotificationProcessorTest.kt
@@ -111,13 +111,13 @@
             .isNotNull()
 
         val processedSummary = nb.build().extras.getCharSequence(EXTRA_SUMMARIZED_CONTENT)
-        assertThat(processedSummary.toString()).isEqualTo("x$summarization")
+        assertThat(processedSummary.toString()).isEqualTo("x $summarization")
 
         val checkSpans = SpannableStringBuilder(processedSummary)
         assertThat(
                 checkSpans.getSpans(
                     /* queryStart = */ 0,
-                    /* queryEnd = */ 1,
+                    /* queryEnd = */ 2,
                     /* kind = */ ImageSpan::class.java,
                 )
             )
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
index 5d8b68e..83fd5dc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
@@ -105,4 +105,52 @@
     fun getWhen_adapter() {
         assertThat(underTest.entryAdapter.`when`).isEqualTo(0)
     }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun isColorized() {
+        assertThat(underTest.entryAdapter.isColorized).isFalse()
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun getSbn() {
+        assertThat(underTest.entryAdapter.sbn).isNull()
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun canDragAndDrop() {
+        assertThat(underTest.entryAdapter.canDragAndDrop()).isFalse()
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun isBubble() {
+        assertThat(underTest.entryAdapter.isBubbleCapable).isFalse()
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun getStyle() {
+        assertThat(underTest.entryAdapter.style).isNull()
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun getSectionBucket() {
+        assertThat(underTest.entryAdapter.sectionBucket).isEqualTo(underTest.bucket)
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun isAmbient() {
+        assertThat(underTest.entryAdapter.isAmbient).isFalse()
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun canShowFullScreen() {
+        assertThat(underTest.entryAdapter.isFullScreenCapable()).isFalse()
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 34dff24..4810813 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -21,12 +21,15 @@
 import static android.app.Notification.CATEGORY_EVENT;
 import static android.app.Notification.CATEGORY_MESSAGE;
 import static android.app.Notification.CATEGORY_REMINDER;
+import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
 import static android.app.Notification.FLAG_PROMOTED_ONGOING;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 
 import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
 import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
+import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_ALERTING;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -35,6 +38,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
 import android.app.Notification;
@@ -664,6 +668,126 @@
         assertThat(entry.getEntryAdapter().getIcons()).isEqualTo(entry.getIcons());
     }
 
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    public void isColorized() {
+        Notification notification = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setColorized(true)
+                .build();
+
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .build();
+        assertThat(entry.getEntryAdapter().isColorized()).isEqualTo(
+                entry.getSbn().getNotification().isColorized());
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    public void getSbn() {
+        Notification notification = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .build();
+
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .build();
+        assertThat(entry.getEntryAdapter().getSbn()).isEqualTo(
+                entry.getSbn());
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    public void canDragAndDrop() {
+        PendingIntent pi = mock(PendingIntent.class);
+        when(pi.isActivity()).thenReturn(true);
+        Notification notification = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setContentIntent(pi)
+                .build();
+
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .build();
+        assertThat(entry.getEntryAdapter().canDragAndDrop()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    public void isBubble() {
+        Notification notification = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setFlag(FLAG_BUBBLE, true)
+                .build();
+
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .build();
+        assertThat(entry.getEntryAdapter().isBubbleCapable()).isEqualTo(entry.isBubble());
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    public void getStyle() {
+        Notification notification = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setStyle(new Notification.BigTextStyle())
+                .build();
+
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .build();
+        assertThat(entry.getEntryAdapter().getStyle()).isEqualTo(entry.getNotificationStyle());
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    public void getSectionBucket() {
+        Notification notification = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setStyle(new Notification.BigTextStyle())
+                .build();
+
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .build();
+        entry.setBucket(BUCKET_ALERTING);
+
+        assertThat(entry.getEntryAdapter().getSectionBucket()).isEqualTo(BUCKET_ALERTING);
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    public void isAmbient() {
+        Notification notification = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .build();
+
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .setImportance(IMPORTANCE_MIN)
+                .build();
+
+        assertThat(entry.getEntryAdapter().isAmbient()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    public void canShowFullScreen() {
+        Notification notification = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setFullScreenIntent(mock(PendingIntent.class), true)
+                .build();
+
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .setImportance(IMPORTANCE_MIN)
+                .build();
+
+        assertThat(entry.getEntryAdapter().isFullScreenCapable()).isTrue();
+    }
+
     private Notification.Action createContextualAction(String title) {
         return new Notification.Action.Builder(
                 Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
index d5b2d3a..4e13dcd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
@@ -20,7 +20,6 @@
 import android.media.session.MediaSession
 import android.platform.test.flag.junit.FlagsParameterization
 import android.provider.Settings
-import android.service.notification.StatusBarNotification
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.dumpManager
@@ -222,16 +221,19 @@
                         mock<ExpandableNotificationRow>().apply {
                             whenever(isMediaRow).thenReturn(true)
                         }
-                    sbn = SbnBuilder().setNotification(
-                        Notification.Builder(context, "channel").setStyle(
-                            MediaStyle().setMediaSession(
-                                MediaSession(
-                                    context,
-                                    "tag"
-                                ).sessionToken
+                    sbn =
+                        SbnBuilder()
+                            .setNotification(
+                                Notification.Builder(context, "channel")
+                                    .setStyle(
+                                        MediaStyle()
+                                            .setMediaSession(
+                                                MediaSession(context, "tag").sessionToken
+                                            )
+                                    )
+                                    .build()
                             )
-                        ).build()
-                    ).build()
+                            .build()
                 }
             collectionListener.onEntryAdded(fakeEntry)
 
@@ -631,6 +633,132 @@
         }
     }
 
+    @Test
+    fun seenNotificationOnKeyguardMarkedAsSeenIfUpdatedBySystemServer() {
+        // GIVEN: Keyguard is showing, not dozing, unseen notification is present
+        keyguardRepository.setKeyguardShowing(true)
+        keyguardRepository.setIsDozing(false)
+        runKeyguardCoordinatorTest {
+            val fakeEntry = NotificationEntryBuilder().build()
+            collectionListener.onEntryAdded(fakeEntry)
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.AOD,
+                to = KeyguardState.LOCKSCREEN,
+                this.testScheduler,
+            )
+            testScheduler.runCurrent()
+
+            // WHEN: five seconds have passed
+            testScheduler.advanceTimeBy(5.seconds)
+            testScheduler.runCurrent()
+
+            // WHEN: Keyguard is no longer showing
+            keyguardRepository.setKeyguardShowing(false)
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Gone),
+                stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE),
+            )
+
+            // WHEN: the notification is updated by the Server
+            collectionListener.onEntryUpdated(fakeEntry, UpdateSource.SystemServer)
+            testScheduler.runCurrent()
+
+            // WHEN: Keyguard is shown again
+            keyguardRepository.setKeyguardShowing(true)
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Lockscreen),
+                stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.AOD),
+            )
+
+            // THEN: The notification is still recognized as "seen" and is filtered out.
+            assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isTrue()
+        }
+    }
+
+    @Test
+    fun seenNotificationOnKeyguardMarkedAsSeenIfUpdatedBySystemUi() {
+        // GIVEN: Keyguard is showing, not dozing, unseen notification is present
+        keyguardRepository.setKeyguardShowing(true)
+        keyguardRepository.setIsDozing(false)
+        runKeyguardCoordinatorTest {
+            val fakeEntry = NotificationEntryBuilder().build()
+            collectionListener.onEntryAdded(fakeEntry)
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.AOD,
+                to = KeyguardState.LOCKSCREEN,
+                this.testScheduler,
+            )
+            testScheduler.runCurrent()
+
+            // WHEN: five seconds have passed
+            testScheduler.advanceTimeBy(5.seconds)
+            testScheduler.runCurrent()
+
+            // WHEN: Keyguard is no longer showing
+            keyguardRepository.setKeyguardShowing(false)
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Gone),
+                stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE),
+            )
+
+            // WHEN: the notification is updated by the SystemUi
+            collectionListener.onEntryUpdated(fakeEntry, UpdateSource.SystemUi)
+            testScheduler.runCurrent()
+
+            // WHEN: Keyguard is shown again
+            keyguardRepository.setKeyguardShowing(true)
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Lockscreen),
+                stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.AOD),
+            )
+
+            // THEN: The notification is still recognized as "seen" and is filtered out.
+            assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isTrue()
+        }
+    }
+
+    @Test
+    fun seenNotificationOnKeyguardMarkedAsUnseenIfUpdatedByApp() {
+        // GIVEN: Keyguard is showing, not dozing, unseen notification is present
+        keyguardRepository.setKeyguardShowing(true)
+        keyguardRepository.setIsDozing(false)
+        runKeyguardCoordinatorTest {
+            val fakeEntry = NotificationEntryBuilder().build()
+            collectionListener.onEntryAdded(fakeEntry)
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.AOD,
+                to = KeyguardState.LOCKSCREEN,
+                this.testScheduler,
+            )
+            testScheduler.runCurrent()
+
+            // WHEN: five seconds have passed
+            testScheduler.advanceTimeBy(5.seconds)
+            testScheduler.runCurrent()
+
+            // WHEN: Keyguard is no longer showing
+            keyguardRepository.setKeyguardShowing(false)
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Gone),
+                stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE),
+            )
+
+            // WHEN: the notification is updated by the App
+            collectionListener.onEntryUpdated(fakeEntry, UpdateSource.App)
+            testScheduler.runCurrent()
+
+            // WHEN: Keyguard is shown again
+            keyguardRepository.setKeyguardShowing(true)
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Lockscreen),
+                stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.AOD),
+            )
+
+            // THEN: The notification is now recognized as "unseen" and is not filtered out.
+            assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isFalse()
+        }
+    }
+
     private fun runKeyguardCoordinatorTest(
         testBlock: suspend KeyguardCoordinatorTestScope.() -> Unit
     ) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
deleted file mode 100644
index 2aa405a..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ /dev/null
@@ -1,1001 +0,0 @@
-/*
- * Copyright (C) 2020 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.collection.coordinator;
-
-import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static junit.framework.Assert.assertFalse;
-
-import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.FlagsParameterization;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.compose.animation.scene.ObservableTransitionState;
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.shared.model.CommunalScenes;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.BrokenWithSceneContainer;
-import com.android.systemui.flags.DisableSceneContainer;
-import com.android.systemui.flags.EnableSceneContainer;
-import com.android.systemui.flags.SceneContainerFlagParameterizationKt;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.keyguard.shared.model.KeyguardState;
-import com.android.systemui.keyguard.shared.model.TransitionState;
-import com.android.systemui.keyguard.shared.model.TransitionStep;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.shade.data.repository.FakeShadeRepository;
-import com.android.systemui.shade.data.repository.ShadeAnimationRepository;
-import com.android.systemui.shade.data.repository.ShadeRepository;
-import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
-import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl;
-import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
-import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
-import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
-import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository;
-import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.kotlin.JavaAdapter;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import kotlinx.coroutines.flow.MutableStateFlow;
-import kotlinx.coroutines.test.TestScope;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.verification.VerificationMode;
-
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
-import java.util.List;
-import java.util.Set;
-
-@SmallTest
-@RunWith(ParameterizedAndroidJunit4.class)
-@TestableLooper.RunWithLooper
-public class VisualStabilityCoordinatorTest extends SysuiTestCase {
-
-    @Parameters(name = "{0}")
-    public static List<FlagsParameterization> getParams() {
-        return SceneContainerFlagParameterizationKt
-                .andSceneContainer(allCombinationsOf(Flags.FLAG_STABILIZE_HEADS_UP_GROUP_V2));
-    }
-
-    private VisualStabilityCoordinator mCoordinator;
-
-    @Mock private DumpManager mDumpManager;
-    @Mock private NotifPipeline mNotifPipeline;
-    @Mock private WakefulnessLifecycle mWakefulnessLifecycle;
-    @Mock private StatusBarStateController mStatusBarStateController;
-    @Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener;
-    @Mock private SeenNotificationsInteractor mSeenNotificationsInteractor;
-    @Mock private HeadsUpRepository mHeadsUpRepository;
-    @Mock private VisibilityLocationProvider mVisibilityLocationProvider;
-    @Mock private VisualStabilityProvider mVisualStabilityProvider;
-    @Mock private VisualStabilityCoordinatorLogger mLogger;
-    @Mock private KeyguardStateController mKeyguardStateController;
-
-    @Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor;
-    @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mSBStateListenerCaptor;
-    @Captor private ArgumentCaptor<NotifStabilityManager> mNotifStabilityManagerCaptor;
-
-    private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
-    private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
-    private FakeExecutor mFakeBackgroundExecutor = new FakeExecutor(mFakeSystemClock);
-    private FakeExecutor mFakeMainExecutor = new FakeExecutor(mFakeSystemClock);
-    private final TestScope mTestScope = mKosmos.getTestScope();
-    private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
-
-    private ShadeAnimationInteractor mShadeAnimationInteractor;
-    private ShadeRepository mShadeRepository;
-    private WakefulnessLifecycle.Observer mWakefulnessObserver;
-    private StatusBarStateController.StateListener mStatusBarStateListener;
-    private NotifStabilityManager mNotifStabilityManager;
-    private NotificationEntry mEntry;
-    private GroupEntry mGroupEntry;
-
-    public VisualStabilityCoordinatorTest(FlagsParameterization flags) {
-        super();
-        mSetFlagsRule.setFlagsParameterization(flags);
-    }
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mShadeRepository = new FakeShadeRepository();
-        mShadeAnimationInteractor = new ShadeAnimationInteractorLegacyImpl(
-                new ShadeAnimationRepository(), mShadeRepository);
-        mCoordinator = new VisualStabilityCoordinator(
-                mFakeBackgroundExecutor,
-                mFakeMainExecutor,
-                mDumpManager,
-                mHeadsUpRepository,
-                mShadeAnimationInteractor,
-                mJavaAdapter,
-                mSeenNotificationsInteractor,
-                mStatusBarStateController,
-                mVisibilityLocationProvider,
-                mVisualStabilityProvider,
-                mWakefulnessLifecycle,
-                mKosmos.getCommunalSceneInteractor(),
-                mKosmos.getShadeInteractor(),
-                mKosmos.getKeyguardTransitionInteractor(),
-                mKeyguardStateController,
-                mLogger);
-
-        when(mHeadsUpRepository.isTrackingHeadsUp()).thenReturn(MutableStateFlow(false));
-        mCoordinator.attach(mNotifPipeline);
-        mTestScope.getTestScheduler().runCurrent();
-
-        // capture arguments:
-        verify(mWakefulnessLifecycle).addObserver(mWakefulnessObserverCaptor.capture());
-        mWakefulnessObserver = mWakefulnessObserverCaptor.getValue();
-
-        verify(mStatusBarStateController).addCallback(mSBStateListenerCaptor.capture());
-        mStatusBarStateListener = mSBStateListenerCaptor.getValue();
-
-        verify(mNotifPipeline).setVisualStabilityManager(mNotifStabilityManagerCaptor.capture());
-        mNotifStabilityManager = mNotifStabilityManagerCaptor.getValue();
-        mNotifStabilityManager.setInvalidationListener(mInvalidateListener);
-
-        mEntry = new NotificationEntryBuilder()
-                .setPkg("testPkg1")
-                .build();
-
-        mGroupEntry = new GroupEntryBuilder()
-                .setSummary(mEntry)
-                .build();
-
-        when(mHeadsUpRepository.isHeadsUpEntry(mEntry.getKey())).thenReturn(false);
-
-        // Whenever we invalidate, the pipeline runs again, so we invalidate the state
-        doAnswer(i -> {
-            mNotifStabilityManager.onBeginRun();
-            return null;
-        }).when(mInvalidateListener).onPluggableInvalidated(eq(mNotifStabilityManager), any());
-    }
-
-    @Test
-    public void testScreenOff_groupAndSectionChangesAllowed() {
-        // GIVEN screen is off, panel isn't expanded and device isn't pulsing
-        setFullyDozed(true);
-        setSleepy(true);
-        setPanelExpanded(false);
-        setPulsing(false);
-
-        // THEN group changes are allowed
-        assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
-        assertTrue(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
-        // THEN section changes are allowed
-        assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-    }
-
-    @Test
-    public void testScreenTurningOff_groupAndSectionChangesNotAllowed() {
-        // GIVEN the screen is turning off (sleepy but partially dozed)
-        setFullyDozed(false);
-        setSleepy(true);
-        setPanelExpanded(true);
-        setPulsing(false);
-
-        // THEN group changes are NOT allowed
-        assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
-        assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
-        // THEN section changes are NOT allowed
-        assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-    }
-
-    @Test
-    public void testScreenTurningOn_groupAndSectionChangesNotAllowed() {
-        // GIVEN the screen is turning on (still fully dozed, not sleepy)
-        setFullyDozed(true);
-        setSleepy(false);
-        setPanelExpanded(true);
-        setPulsing(false);
-
-        // THEN group changes are NOT allowed
-        assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
-        assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
-        // THEN section changes are NOT allowed
-        assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-    }
-
-    @Test
-    public void testPanelNotExpanded_groupAndSectionChangesAllowed() {
-        // GIVEN screen is on but the panel isn't expanded and device isn't pulsing
-        setFullyDozed(false);
-        setSleepy(false);
-        setPanelExpanded(false);
-        setPulsing(false);
-
-        // THEN group changes are allowed
-        assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
-        assertTrue(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
-        // THEN section changes are allowed
-        assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-    }
-
-    @Test
-    public void testPanelExpanded_groupAndSectionChangesNotAllowed() {
-        // GIVEN the panel true expanded and device isn't pulsing
-        setFullyDozed(false);
-        setSleepy(false);
-        setPanelExpanded(true);
-        setPulsing(false);
-
-        // THEN group changes are NOT allowed
-        assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
-        assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
-        // THEN section changes are NOT allowed
-        assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-    }
-
-    @Test
-    @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
-    public void testLockscreenPartlyShowing_groupAndSectionChangesNotAllowed() {
-        // GIVEN the panel true expanded and device isn't pulsing
-        setFullyDozed(false);
-        setSleepy(false);
-        setLockscreenShowing(0.5f);
-        setPulsing(false);
-
-        // THEN group changes are NOT allowed
-        assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
-        assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
-        // THEN section changes are NOT allowed
-        assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-    }
-
-    @Test
-    @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
-    public void testLockscreenFullyShowing_groupAndSectionChangesNotAllowed() {
-        // GIVEN the panel true expanded and device isn't pulsing
-        setFullyDozed(false);
-        setSleepy(false);
-        setLockscreenShowing(1.0f);
-        setPulsing(false);
-
-        // THEN group changes are NOT allowed
-        assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
-        assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
-        // THEN section changes are NOT allowed
-        assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-    }
-
-    @Test
-    public void testPulsing_screenOff_groupAndSectionChangesNotAllowed() {
-        // GIVEN the device is pulsing and screen is off
-        setFullyDozed(true);
-        setSleepy(true);
-        setPulsing(true);
-
-        // THEN group changes are NOT allowed
-        assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
-        assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
-        // THEN section changes are NOT allowed
-        assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-    }
-
-    @Test
-    public void testPulsing_panelNotExpanded_groupAndSectionChangesNotAllowed() {
-        // GIVEN the device is pulsing and screen is off with the panel not expanded
-        setFullyDozed(true);
-        setSleepy(true);
-        setPanelExpanded(false);
-        setPulsing(true);
-
-        // THEN group changes are NOT allowed
-        assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
-        assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
-        // THEN section changes are NOT allowed
-        assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-    }
-
-    @Test
-    public void testOverrideReorderingSuppression_onlySectionChangesAllowed() {
-        // GIVEN section changes typically wouldn't be allowed because the panel is expanded and
-        // we're not pulsing
-        setFullyDozed(false);
-        setSleepy(false);
-        setPanelExpanded(true);
-        setPulsing(true);
-
-        // WHEN we temporarily allow section changes for this notification entry
-        mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
-
-        // THEN group changes aren't allowed
-        assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
-        assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
-        // THEN section changes are allowed for this notification but not other notifications
-        assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-        assertFalse(mNotifStabilityManager.isSectionChangeAllowed(
-                new NotificationEntryBuilder()
-                        .setPkg("testPkg2")
-                        .build()));
-    }
-
-    @Test
-    public void testTemporarilyAllowSectionChanges_callsInvalidate() {
-        // GIVEN section changes typically wouldn't be allowed because the panel is expanded
-        setFullyDozed(false);
-        setSleepy(false);
-        setPanelExpanded(true);
-        setPulsing(false);
-
-        // WHEN we temporarily allow section changes for this notification entry
-        mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.uptimeMillis());
-
-        // THEN the notification list is invalidated
-        verifyStabilityManagerWasInvalidated(times(1));
-    }
-
-    @Test
-    public void testTemporarilyAllowSectionChanges_noInvalidationCalled() {
-        // GIVEN section changes typically WOULD be allowed
-        setFullyDozed(true);
-        setSleepy(true);
-        setPanelExpanded(false);
-        setPulsing(false);
-
-        // WHEN we temporarily allow section changes for this notification entry
-        mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
-
-        // THEN invalidate is not called because this entry was never suppressed from reordering
-        verifyStabilityManagerWasInvalidated(never());
-    }
-
-    @Test
-    public void testTemporarilyAllowSectionChangesTimeout() {
-        // GIVEN section changes typically WOULD be allowed
-        setFullyDozed(true);
-        setSleepy(true);
-        setPanelExpanded(false);
-        setPulsing(false);
-        assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-
-        // WHEN we temporarily allow section changes for this notification entry
-        mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
-
-        // THEN invalidate is not called because this entry was never suppressed from reordering;
-        // THEN section changes are allowed for this notification
-        verifyStabilityManagerWasInvalidated(never());
-        assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-
-        // WHEN we're pulsing (now disallowing reordering)
-        setPulsing(true);
-
-        // THEN we're still allowed to reorder this section because it's still in the list of
-        // notifications to allow section changes
-        assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-
-        // WHEN the timeout for the temporarily allow section reordering runnable is finsihed
-        mFakeBackgroundExecutor.advanceClockToNext();
-        mFakeBackgroundExecutor.runNextReady();
-
-        // THEN section changes aren't allowed anymore
-        assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-    }
-
-    @Test
-    public void testTemporarilyAllowSectionChanges_isPulsingChangeBeforeTimeout() {
-        // GIVEN section changes typically wouldn't be allowed because the device is pulsing
-        setFullyDozed(true);
-        setSleepy(true);
-        setPanelExpanded(false);
-        setPulsing(true);
-
-        // WHEN we temporarily allow section changes for this notification entry
-        mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
-        // can now reorder, so invalidates
-        verifyStabilityManagerWasInvalidated(times(1));
-
-        // WHEN reordering is now allowed because device isn't pulsing anymore
-        setPulsing(false);
-
-        // THEN invalidate isn't called a second time since reordering was already allowed
-        verifyStabilityManagerWasInvalidated(times(1));
-    }
-
-    @Test
-    public void testMovingVisibleHeadsUpNotAllowed() {
-        // GIVEN stability enforcing conditions
-        setPanelExpanded(true);
-        setSleepy(false);
-
-        // WHEN a notification is alerting and visible
-        when(mHeadsUpRepository.isHeadsUpEntry(mEntry.getKey())).thenReturn(true);
-        when(mVisibilityLocationProvider.isInVisibleLocation(any(NotificationEntry.class)))
-                .thenReturn(true);
-
-        // VERIFY the notification cannot be reordered
-        assertThat(mNotifStabilityManager.isEntryReorderingAllowed(mEntry)).isFalse();
-        assertThat(mNotifStabilityManager.isSectionChangeAllowed(mEntry)).isFalse();
-    }
-
-    @Test
-    public void testMovingInvisibleHeadsUpAllowed() {
-        // GIVEN stability enforcing conditions
-        setPanelExpanded(true);
-        setSleepy(false);
-
-        // WHEN a notification is alerting but not visible
-        when(mHeadsUpRepository.isHeadsUpEntry(mEntry.getKey())).thenReturn(true);
-        when(mVisibilityLocationProvider.isInVisibleLocation(any(NotificationEntry.class)))
-                .thenReturn(false);
-
-        // VERIFY the notification can be reordered
-        assertThat(mNotifStabilityManager.isEntryReorderingAllowed(mEntry)).isTrue();
-        assertThat(mNotifStabilityManager.isSectionChangeAllowed(mEntry)).isTrue();
-    }
-
-    @Test
-    public void testNeverSuppressedChanges_noInvalidationCalled() {
-        // GIVEN no notifications are currently being suppressed from grouping nor being sorted
-
-        // WHEN device isn't pulsing anymore
-        setPulsing(false);
-
-        // WHEN fully dozed
-        setFullyDozed(true);
-
-        // WHEN sleepy
-        setSleepy(true);
-
-        // WHEN panel isn't expanded
-        setPanelExpanded(false);
-
-        // THEN we never see any calls to invalidate since there weren't any notifications that
-        // were being suppressed from grouping or section changes
-        verifyStabilityManagerWasInvalidated(never());
-    }
-
-    @Test
-    public void testNotSuppressingGroupChangesAnymore_invalidationCalled() {
-        // GIVEN visual stability is being maintained b/c panel is expanded
-        setPulsing(false);
-        setFullyDozed(false);
-        setSleepy(false);
-        setPanelExpanded(true);
-
-        assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
-        assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
-        // WHEN the panel isn't expanded anymore
-        setPanelExpanded(false);
-
-        //  invalidate is called because we were previously suppressing a group change
-        verifyStabilityManagerWasInvalidated(times(1));
-    }
-
-    @Test
-    public void testNotLaunchingActivityAnymore_invalidationCalled() {
-        // GIVEN visual stability is being maintained b/c animation is playing
-        setActivityLaunching(true);
-
-        assertFalse(mNotifStabilityManager.isPipelineRunAllowed());
-
-        // WHEN the animation has stopped playing
-        setActivityLaunching(false);
-
-        // invalidate is called, b/c we were previously suppressing the pipeline from running
-        verifyStabilityManagerWasInvalidated(times(1));
-    }
-
-    @Test
-    public void testNotCollapsingPanelAnymore_invalidationCalled() {
-        // GIVEN visual stability is being maintained b/c animation is playing
-        setPanelCollapsing(true);
-
-        assertFalse(mNotifStabilityManager.isPipelineRunAllowed());
-
-        // WHEN the animation has stopped playing
-        setPanelCollapsing(false);
-
-        // invalidate is called, b/c we were previously suppressing the pipeline from running
-        verifyStabilityManagerWasInvalidated(times(1));
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_CHECK_LOCKSCREEN_GONE_TRANSITION)
-    @DisableSceneContainer
-    public void testNotLockscreenInGoneTransitionLegacy_invalidationCalled() {
-        // GIVEN visual stability is being maintained b/c animation is playing
-        doReturn(true).when(mKeyguardStateController).isKeyguardFadingAway();
-        mCoordinator.mKeyguardFadeAwayAnimationCallback.onKeyguardFadingAwayChanged();
-
-        assertFalse(mNotifStabilityManager.isPipelineRunAllowed());
-
-        // WHEN the animation has stopped playing
-        doReturn(false).when(mKeyguardStateController).isKeyguardFadingAway();
-        mCoordinator.mKeyguardFadeAwayAnimationCallback.onKeyguardFadingAwayChanged();
-
-        // invalidate is called, b/c we were previously suppressing the pipeline from running
-        verifyStabilityManagerWasInvalidated(times(1));
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_CHECK_LOCKSCREEN_GONE_TRANSITION)
-    @EnableSceneContainer
-    @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
-    public void testNotLockscreenInGoneTransition_invalidationCalled() {
-        // GIVEN visual stability is being maintained b/c animation is playing
-        mKosmos.getKeyguardTransitionRepository().sendTransitionStepJava(
-                mTestScope, new TransitionStep(
-                        KeyguardState.LOCKSCREEN,
-                        KeyguardState.GONE,
-                        1f,
-                        TransitionState.RUNNING),  /* validateStep = */ false);
-        mTestScope.getTestScheduler().runCurrent();
-        assertFalse(mNotifStabilityManager.isPipelineRunAllowed());
-
-        // WHEN the animation has stopped playing
-        mKosmos.getKeyguardTransitionRepository().sendTransitionStepJava(
-                mTestScope, new TransitionStep(
-                        KeyguardState.LOCKSCREEN,
-                        KeyguardState.GONE,
-                        1f,
-                        TransitionState.FINISHED),  /* validateStep = */ false);
-        mTestScope.getTestScheduler().runCurrent();
-
-        // invalidate is called, b/c we were previously suppressing the pipeline from running
-        verifyStabilityManagerWasInvalidated(times(1));
-    }
-
-    @Test
-    public void testNeverSuppressPipelineRunFromPanelCollapse_noInvalidationCalled() {
-        // GIVEN animation is playing
-        setPanelCollapsing(true);
-
-        // WHEN the animation has stopped playing
-        setPanelCollapsing(false);
-
-        // THEN invalidate is not called, b/c nothing has been suppressed
-        verifyStabilityManagerWasInvalidated(never());
-    }
-
-    @Test
-    public void testNeverSuppressPipelineRunFromLaunchActivity_noInvalidationCalled() {
-        // GIVEN animation is playing
-        setActivityLaunching(true);
-
-        // WHEN the animation has stopped playing
-        setActivityLaunching(false);
-
-        // THEN invalidate is not called, b/c nothing has been suppressed
-        verifyStabilityManagerWasInvalidated(never());
-    }
-
-    @Test
-    public void testNotSuppressingEntryReorderingAnymoreWillInvalidate() {
-        // GIVEN visual stability is being maintained b/c panel is expanded
-        setPulsing(false);
-        setFullyDozed(false);
-        setSleepy(false);
-        setPanelExpanded(true);
-        setCommunalShowing(false);
-
-        assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
-        // The pipeline still has to report back that entry reordering was suppressed
-        mNotifStabilityManager.onEntryReorderSuppressed();
-
-        // WHEN the panel isn't expanded anymore
-        setPanelExpanded(false);
-
-        //  invalidate is called because we were previously suppressing an entry reorder
-        verifyStabilityManagerWasInvalidated(times(1));
-    }
-
-    @Test
-    @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
-    public void testCommunalShowingWillNotSuppressReordering() {
-        // GIVEN panel is expanded, communal is showing, and QS is collapsed
-        setPulsing(false);
-        setFullyDozed(false);
-        setSleepy(false);
-        setPanelExpanded(true);
-        setQsExpanded(false);
-        setCommunalShowing(true);
-
-        // Reordering should be allowed
-        assertTrue(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
-    }
-
-    @Test
-    public void testQsExpandedOverCommunalWillSuppressReordering() {
-        // GIVEN panel is expanded and communal is showing, but QS is expanded
-        setPulsing(false);
-        setFullyDozed(false);
-        setSleepy(false);
-        setPanelExpanded(true);
-        setQsExpanded(true);
-        setCommunalShowing(true);
-
-        // Reordering should not be allowed
-        assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
-    }
-
-    @Test
-    public void testQueryingEntryReorderingButNotReportingReorderSuppressedDoesNotInvalidate() {
-        // GIVEN visual stability is being maintained b/c panel is expanded
-        setPulsing(false);
-        setFullyDozed(false);
-        setSleepy(false);
-        setPanelExpanded(true);
-
-        assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
-
-        // WHEN the panel isn't expanded anymore
-        setPanelExpanded(false);
-
-        // invalidate is not called because we were not told that an entry reorder was suppressed
-        verifyStabilityManagerWasInvalidated(never());
-    }
-
-    @Test
-    public void testHeadsUp_allowedToChangeGroupAndSection() {
-        // GIVEN group + section changes disallowed
-        setFullyDozed(false);
-        setSleepy(false);
-        setPanelExpanded(true);
-        setPulsing(true);
-        assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
-        assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-        assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-
-        // GIVEN mEntry is a HUN
-        when(mHeadsUpRepository.isHeadsUpEntry(mEntry.getKey())).thenReturn(true);
-
-        // THEN group + section changes are allowed
-        assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
-        assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-
-        // BUT pruning the group for which this is the summary would still NOT be allowed.
-        assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-    }
-
-    @Test
-    public void everyChangeAllowed_onReorderingEnabled_legacy() {
-        assumeFalse(StabilizeHeadsUpGroup.isEnabled());
-        // GIVEN - reordering is allowed.
-        setPulsing(false);
-        setPanelExpanded(false);
-
-        // THEN
-        assertThat(mNotifStabilityManager.isEveryChangeAllowed()).isTrue();
-        assertThat(mNotifStabilityManager.isGroupChangeAllowed(any())).isTrue();
-        assertThat(mNotifStabilityManager.isGroupPruneAllowed(any())).isTrue();
-        assertThat(mNotifStabilityManager.isSectionChangeAllowed(any())).isTrue();
-        assertThat(mNotifStabilityManager.isEntryReorderingAllowed(any())).isTrue();
-    }
-
-    @Test
-    public void everyChangeAllowed_noActiveHeadsUpGroup_onReorderingEnabled() {
-        assumeTrue(StabilizeHeadsUpGroup.isEnabled());
-        // GIVEN - reordering is allowed.
-        setPulsing(false);
-        setPanelExpanded(false);
-
-        // GIVEN - empty heads-up-group keys
-        mCoordinator.setHeadsUpGroupKeys(Set.of());
-
-        // THEN
-        assertThat(mNotifStabilityManager.isEveryChangeAllowed()).isTrue();
-        assertThat(mNotifStabilityManager.isGroupChangeAllowed(any())).isTrue();
-        assertThat(mNotifStabilityManager.isGroupPruneAllowed(any())).isTrue();
-        assertThat(mNotifStabilityManager.isSectionChangeAllowed(any())).isTrue();
-        assertThat(mNotifStabilityManager.isEntryReorderingAllowed(any())).isTrue();
-    }
-
-    @Test
-    public void everyChangeDisallowed_activeHeadsUpGroup_onReorderingEnabled() {
-        assumeTrue(StabilizeHeadsUpGroup.isEnabled());
-        // GIVEN - reordering is allowed.
-        setPulsing(false);
-        setPanelExpanded(false);
-
-        // GIVEN - there is a group heads-up.
-        mCoordinator.setHeadsUpGroupKeys(Set.of("heads_up_group_key"));
-
-        // THEN
-        assertThat(mNotifStabilityManager.isEveryChangeAllowed()).isFalse();
-    }
-
-    @Test
-    public void nonHeadsUpGroup_changesAllowed_onReorderingEnabled() {
-        assumeTrue(StabilizeHeadsUpGroup.isEnabled());
-        // GIVEN - reordering is allowed.
-        setPulsing(false);
-        setPanelExpanded(false);
-
-        //  GIVEN - there is a group heads-up.
-        String headsUpGroupKey = "heads_up_group_key";
-        mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
-        when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
-
-        // GIVEN - HUN Group Summary
-        final NotificationEntry nonHeadsUpGroupSummary = mock(NotificationEntry.class);
-        when(nonHeadsUpGroupSummary.getKey()).thenReturn("non_heads_up_group_key");
-        when(nonHeadsUpGroupSummary.isSummaryWithChildren()).thenReturn(true);
-        final GroupEntry nonHeadsUpGroupEntry = mock(GroupEntry.class);
-        when(nonHeadsUpGroupEntry.getSummary()).thenReturn(nonHeadsUpGroupSummary);
-        when(nonHeadsUpGroupEntry.getRepresentativeEntry()).thenReturn(nonHeadsUpGroupSummary);
-
-        // THEN
-        assertThat(mNotifStabilityManager.isGroupPruneAllowed(nonHeadsUpGroupEntry)).isTrue();
-        assertThat(mNotifStabilityManager.isEntryReorderingAllowed(nonHeadsUpGroupEntry)).isTrue();
-    }
-
-    @Test
-    public void headsUpGroup_changesDisallowed_onReorderingEnabled() {
-        assumeTrue(StabilizeHeadsUpGroup.isEnabled());
-        // GIVEN - reordering is allowed.
-        setPulsing(false);
-        setPanelExpanded(false);
-
-        //  GIVEN - there is a group heads-up.
-        final String headsUpGroupKey = "heads_up_group_key";
-        mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
-        when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
-
-        // GIVEN - HUN Group
-        final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class);
-        when(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false);
-        when(headsUpGroupSummary.getKey()).thenReturn(headsUpGroupKey);
-        when(headsUpGroupSummary.isSummaryWithChildren()).thenReturn(true);
-
-        final GroupEntry headsUpGroupEntry = mock(GroupEntry.class);
-        when(headsUpGroupEntry.getSummary()).thenReturn(headsUpGroupSummary);
-        when(headsUpGroupEntry.getRepresentativeEntry()).thenReturn(headsUpGroupSummary);
-
-        when(headsUpGroupSummary.getParent()).thenReturn(headsUpGroupEntry);
-
-        // GIVEN - HUN is in visible location
-        when(mVisibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary)).thenReturn(true);
-
-        // THEN
-        assertThat(mNotifStabilityManager.isGroupPruneAllowed(headsUpGroupEntry)).isFalse();
-        assertThat(mNotifStabilityManager.isEntryReorderingAllowed(headsUpGroupEntry)).isFalse();
-    }
-
-    @Test
-    public void headsUpGroupSummaries_changesDisallowed_onReorderingEnabled() {
-        assumeTrue(StabilizeHeadsUpGroup.isEnabled());
-        // GIVEN - reordering is allowed.
-        setPulsing(false);
-        setPanelExpanded(false);
-
-        //  GIVEN - there is a group heads-up.
-        final String headsUpGroupKey = "heads_up_group_key";
-        mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
-        when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
-
-        // GIVEN - HUN Group
-        final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class);
-        when(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false);
-        when(headsUpGroupSummary.getKey()).thenReturn(headsUpGroupKey);
-        when(headsUpGroupSummary.isSummaryWithChildren()).thenReturn(true);
-
-        final GroupEntry headsUpGroupEntry = mock(GroupEntry.class);
-        when(headsUpGroupEntry.getSummary()).thenReturn(headsUpGroupSummary);
-        when(headsUpGroupEntry.getRepresentativeEntry()).thenReturn(headsUpGroupSummary);
-
-        when(headsUpGroupSummary.getParent()).thenReturn(headsUpGroupEntry);
-
-        // GIVEN - HUN is in visible location
-        when(mVisibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary)).thenReturn(true);
-
-        // THEN
-        assertThat(mNotifStabilityManager.isGroupChangeAllowed(headsUpGroupSummary)).isFalse();
-        assertThat(mNotifStabilityManager.isEntryReorderingAllowed(headsUpGroupSummary)).isFalse();
-        assertThat(mNotifStabilityManager.isSectionChangeAllowed(headsUpGroupSummary)).isFalse();
-    }
-
-    @Test
-    public void notificationInNonHUNGroup_changesAllowed_onReorderingEnabled() {
-        assumeTrue(StabilizeHeadsUpGroup.isEnabled());
-        // GIVEN - reordering is allowed.
-        setPulsing(false);
-        setPanelExpanded(false);
-
-        //  GIVEN - there is a group heads-up.
-        String headsUpGroupKey = "heads_up_group_key";
-        mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
-        when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
-
-        // GIVEN - non HUN parent Group Summary
-        final NotificationEntry groupSummary = mock(NotificationEntry.class);
-        when(groupSummary.getKey()).thenReturn("non_heads_up_group_key");
-        when(groupSummary.isSummaryWithChildren()).thenReturn(true);
-
-        final GroupEntry nonHeadsUpGroupEntry = mock(GroupEntry.class);
-        when(nonHeadsUpGroupEntry.getSummary()).thenReturn(groupSummary);
-        when(nonHeadsUpGroupEntry.getRepresentativeEntry()).thenReturn(groupSummary);
-
-        // GIVEN - child entry in a non heads-up group.
-        final NotificationEntry childEntry = mock(NotificationEntry.class);
-        when(childEntry.rowIsChildInGroup()).thenReturn(true);
-        when(childEntry.getParent()).thenReturn(nonHeadsUpGroupEntry);
-        when(childEntry.getParent()).thenReturn(nonHeadsUpGroupEntry);
-
-        // THEN
-        assertThat(mNotifStabilityManager.isGroupChangeAllowed(childEntry)).isTrue();
-        assertThat(mNotifStabilityManager.isSectionChangeAllowed(childEntry)).isTrue();
-        assertThat(mNotifStabilityManager.isEntryReorderingAllowed(nonHeadsUpGroupEntry)).isTrue();
-    }
-
-    @Test
-    public void notificationInHUNGroup_changesDisallowed_reorderingEnabled() {
-        assumeTrue(StabilizeHeadsUpGroup.isEnabled());
-        // GIVEN - reordering is allowed.
-        setPulsing(false);
-        setPanelExpanded(false);
-
-        // GIVEN - there is a group heads-up.
-        final String headsUpGroupKey = "heads_up_group_key";
-        mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
-        when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
-
-        // GIVEN - HUN Group Summary
-        final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class);
-        when(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false);
-        when(headsUpGroupSummary.getKey()).thenReturn(headsUpGroupKey);
-        when(headsUpGroupSummary.isSummaryWithChildren()).thenReturn(true);
-
-        final GroupEntry nonHeadsUpGroupEntry = mock(GroupEntry.class);
-        when(nonHeadsUpGroupEntry.getSummary()).thenReturn(headsUpGroupSummary);
-        when(nonHeadsUpGroupEntry.getRepresentativeEntry()).thenReturn(headsUpGroupSummary);
-
-        // GIVEN - child entry in a non heads-up group.
-        final NotificationEntry childEntry = mock(NotificationEntry.class);
-        when(childEntry.rowIsChildInGroup()).thenReturn(true);
-        when(childEntry.getParent()).thenReturn(nonHeadsUpGroupEntry);
-
-        // GIVEN - HUN is in visible location
-        when(mVisibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary)).thenReturn(true);
-
-        // THEN
-        assertThat(mNotifStabilityManager.isGroupChangeAllowed(childEntry)).isFalse();
-        assertThat(mNotifStabilityManager.isSectionChangeAllowed(childEntry)).isFalse();
-        assertThat(mNotifStabilityManager.isEntryReorderingAllowed(childEntry)).isFalse();
-    }
-
-    private void verifyStabilityManagerWasInvalidated(VerificationMode mode) {
-        verify(mInvalidateListener, mode).onPluggableInvalidated(eq(mNotifStabilityManager), any());
-    }
-
-    private void setActivityLaunching(boolean activityLaunching) {
-        mShadeAnimationInteractor.setIsLaunchingActivity(activityLaunching);
-        mTestScope.getTestScheduler().runCurrent();
-    }
-
-    private void setPanelCollapsing(boolean collapsing) {
-        mShadeRepository.setLegacyIsClosing(collapsing);
-        mTestScope.getTestScheduler().runCurrent();
-    }
-
-    private void setCommunalShowing(boolean isShowing) {
-        final MutableStateFlow<ObservableTransitionState> showingFlow =
-                MutableStateFlow(
-                        new ObservableTransitionState.Idle(
-                                isShowing ? CommunalScenes.Communal : CommunalScenes.Blank)
-                );
-        mKosmos.getCommunalSceneInteractor().setTransitionState(showingFlow);
-        mTestScope.getTestScheduler().runCurrent();
-    }
-
-    private void setQsExpanded(boolean isExpanded) {
-        mKosmos.getShadeRepository().setQsExpansion(isExpanded ? 1.0f : 0.0f);
-        mTestScope.getTestScheduler().runCurrent();
-    }
-
-    private void setPulsing(boolean pulsing) {
-        mStatusBarStateListener.onPulsingChanged(pulsing);
-    }
-
-    private void setFullyDozed(boolean fullyDozed) {
-        float dozeAmount = fullyDozed ? 1 : 0;
-        mStatusBarStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
-    }
-
-    private void setSleepy(boolean sleepy) {
-        if (sleepy) {
-            mWakefulnessObserver.onFinishedGoingToSleep();
-        } else {
-            mWakefulnessObserver.onStartedWakingUp();
-        }
-    }
-
-    private void setPanelExpanded(boolean expanded) {
-        setPanelExpandedAndLockscreenShowing(expanded, /* lockscreenShowing = */ 0.0f);
-    }
-
-    private void setLockscreenShowing(float lockscreenShowing) {
-        setPanelExpandedAndLockscreenShowing(/* panelExpanded = */ false, lockscreenShowing);
-    }
-
-    private void setPanelExpandedAndLockscreenShowing(boolean panelExpanded,
-            float lockscreenShowing) {
-        if (SceneContainerFlag.isEnabled()) {
-            mStatusBarStateListener.onExpandedChanged(panelExpanded);
-            mKosmos.getKeyguardTransitionRepository().sendTransitionStepJava(
-                    mTestScope,
-                    makeLockscreenTransitionStep(lockscreenShowing),
-                    /* validateStep = */ false);
-        } else {
-            mStatusBarStateListener.onExpandedChanged(panelExpanded || lockscreenShowing > 0.0f);
-        }
-    }
-
-    private TransitionStep makeLockscreenTransitionStep(float value) {
-        if (value <= 0.0f) {
-            return new TransitionStep(KeyguardState.GONE);
-        } else if (value >= 1.0f) {
-            return new TransitionStep(KeyguardState.LOCKSCREEN);
-        } else {
-            return new TransitionStep(
-                    KeyguardState.GONE,
-                    KeyguardState.LOCKSCREEN,
-                    value,
-                    TransitionState.RUNNING);
-        }
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
new file mode 100644
index 0000000..ef0a416
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (C) 2020 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.collection.coordinator
+
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.flags.BrokenWithSceneContainer
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.andSceneContainer
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.wakefulnessLifecycle
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.data.repository.ShadeAnimationRepository
+import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
+import com.android.systemui.shade.domain.interactor.shadeAnimationInteractor
+import com.android.systemui.statusbar.notification.VisibilityLocationProvider
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable.PluggableListener
+import com.android.systemui.statusbar.notification.collection.notifPipeline
+import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
+import com.android.systemui.statusbar.notification.stack.data.repository.FakeHeadsUpNotificationRepository
+import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
+import com.android.systemui.statusbar.notification.visibilityLocationProvider
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.fakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runTest
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import org.mockito.verification.VerificationMode
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+@RunWithLooper
+class VisualStabilityCoordinatorTest(flags: FlagsParameterization) : SysuiTestCase() {
+    private val kosmos = testKosmos().apply { testCase = this@VisualStabilityCoordinatorTest }
+
+    private val invalidateListener: PluggableListener<NotifStabilityManager> = mock()
+    private val headsUpRepository = kosmos.headsUpNotificationRepository
+    private val visibilityLocationProvider: VisibilityLocationProvider =
+        kosmos.visibilityLocationProvider
+    private val keyguardStateController: KeyguardStateController = kosmos.keyguardStateController
+
+    private val fakeSystemClock = kosmos.fakeSystemClock
+    private val fakeBackgroundExecutor = kosmos.fakeExecutor
+    private val testScope = kosmos.testScope
+
+    private val shadeRepository = kosmos.fakeShadeRepository
+    private lateinit var wakefulnessObserver: WakefulnessLifecycle.Observer
+    private lateinit var statusBarStateListener: StatusBarStateController.StateListener
+    private lateinit var notifStabilityManager: NotifStabilityManager
+    private lateinit var entry: NotificationEntry
+    private lateinit var groupEntry: GroupEntry
+
+    private val underTest: VisualStabilityCoordinator by lazy { kosmos.visualStabilityCoordinator }
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf(Flags.FLAG_STABILIZE_HEADS_UP_GROUP_V2)
+                .andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
+    @Before
+    fun setUp() {
+        configureKosmos()
+
+        underTest.attach(kosmos.notifPipeline)
+        testScope.testScheduler.runCurrent()
+
+        // capture arguments:
+        wakefulnessObserver = withArgCaptor {
+            verify(kosmos.wakefulnessLifecycle).addObserver(capture())
+        }
+        statusBarStateListener = withArgCaptor {
+            verify(kosmos.statusBarStateController).addCallback(capture())
+        }
+        notifStabilityManager = withArgCaptor {
+            verify(kosmos.notifPipeline).setVisualStabilityManager(capture())
+        }
+        notifStabilityManager.setInvalidationListener(invalidateListener)
+
+        entry = NotificationEntryBuilder().setPkg("testPkg1").build()
+
+        groupEntry = GroupEntryBuilder().setSummary(entry).build()
+
+        // Whenever we invalidate, the pipeline runs again, so we invalidate the state
+        whenever(invalidateListener.onPluggableInvalidated(eq(notifStabilityManager), any()))
+            .doAnswer { _ ->
+                notifStabilityManager.onBeginRun()
+                null
+            }
+    }
+
+    private fun configureKosmos() {
+        kosmos.statusBarStateController = mock()
+        // TODO(377868472) only override this when SceneContainer is disabled
+        kosmos.shadeAnimationInteractor =
+            ShadeAnimationInteractorLegacyImpl(ShadeAnimationRepository(), shadeRepository)
+    }
+
+    @Test
+    fun testScreenOff_groupAndSectionChangesAllowed() =
+        testScope.runTest {
+            // GIVEN screen is off, panel isn't expanded and device isn't pulsing
+            setFullyDozed(true)
+            setSleepy(true)
+            setPanelExpanded(false)
+            setPulsing(false)
+
+            // THEN group changes are allowed
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isTrue()
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isTrue()
+
+            // THEN section changes are allowed
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+        }
+
+    @Test
+    fun testScreenTurningOff_groupAndSectionChangesNotAllowed() =
+        testScope.runTest {
+            // GIVEN the screen is turning off (sleepy but partially dozed)
+            setFullyDozed(false)
+            setSleepy(true)
+            setPanelExpanded(true)
+            setPulsing(false)
+
+            // THEN group changes are NOT allowed
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+            // THEN section changes are NOT allowed
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+        }
+
+    @Test
+    fun testScreenTurningOn_groupAndSectionChangesNotAllowed() =
+        testScope.runTest {
+            // GIVEN the screen is turning on (still fully dozed, not sleepy)
+            setFullyDozed(true)
+            setSleepy(false)
+            setPanelExpanded(true)
+            setPulsing(false)
+
+            // THEN group changes are NOT allowed
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+            // THEN section changes are NOT allowed
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+        }
+
+    @Test
+    fun testPanelNotExpanded_groupAndSectionChangesAllowed() =
+        testScope.runTest {
+            // GIVEN screen is on but the panel isn't expanded and device isn't pulsing
+            setFullyDozed(false)
+            setSleepy(false)
+            setPanelExpanded(false)
+            setPulsing(false)
+
+            // THEN group changes are allowed
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isTrue()
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isTrue()
+
+            // THEN section changes are allowed
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+        }
+
+    @Test
+    fun testPanelExpanded_groupAndSectionChangesNotAllowed() =
+        testScope.runTest {
+            // GIVEN the panel true expanded and device isn't pulsing
+            setFullyDozed(false)
+            setSleepy(false)
+            setPanelExpanded(true)
+            setPulsing(false)
+
+            // THEN group changes are NOT allowed
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+            // THEN section changes are NOT allowed
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+        }
+
+    @Test
+    @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
+    fun testLockscreenPartlyShowing_groupAndSectionChangesNotAllowed() =
+        testScope.runTest {
+            // GIVEN the panel true expanded and device isn't pulsing
+            setFullyDozed(false)
+            setSleepy(false)
+            setLockscreenShowing(0.5f)
+            setPulsing(false)
+
+            // THEN group changes are NOT allowed
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+            // THEN section changes are NOT allowed
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+        }
+
+    @Test
+    @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
+    fun testLockscreenFullyShowing_groupAndSectionChangesNotAllowed() =
+        testScope.runTest {
+            // GIVEN the panel true expanded and device isn't pulsing
+            setFullyDozed(false)
+            setSleepy(false)
+            setLockscreenShowing(1.0f)
+            setPulsing(false)
+
+            // THEN group changes are NOT allowed
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+            // THEN section changes are NOT allowed
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+        }
+
+    @Test
+    fun testPulsing_screenOff_groupAndSectionChangesNotAllowed() =
+        testScope.runTest {
+            // GIVEN the device is pulsing and screen is off
+            setFullyDozed(true)
+            setSleepy(true)
+            setPulsing(true)
+
+            // THEN group changes are NOT allowed
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+            // THEN section changes are NOT allowed
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+        }
+
+    @Test
+    fun testPulsing_panelNotExpanded_groupAndSectionChangesNotAllowed() =
+        testScope.runTest {
+            // GIVEN the device is pulsing and screen is off with the panel not expanded
+            setFullyDozed(true)
+            setSleepy(true)
+            setPanelExpanded(false)
+            setPulsing(true)
+
+            // THEN group changes are NOT allowed
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+            // THEN section changes are NOT allowed
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+        }
+
+    @Test
+    fun testOverrideReorderingSuppression_onlySectionChangesAllowed() =
+        testScope.runTest {
+            // GIVEN section changes typically wouldn't be allowed because the panel is expanded and
+            // we're not pulsing
+            setFullyDozed(false)
+            setSleepy(false)
+            setPanelExpanded(true)
+            setPulsing(true)
+
+            // WHEN we temporarily allow section changes for this notification entry
+            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+
+            // THEN group changes aren't allowed
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+            // THEN section changes are allowed for this notification but not other notifications
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+            assertThat(
+                    notifStabilityManager.isSectionChangeAllowed(
+                        NotificationEntryBuilder().setPkg("testPkg2").build()
+                    )
+                )
+                .isFalse()
+        }
+
+    @Test
+    fun testTemporarilyAllowSectionChanges_callsInvalidate() =
+        testScope.runTest {
+            // GIVEN section changes typically wouldn't be allowed because the panel is expanded
+            setFullyDozed(false)
+            setSleepy(false)
+            setPanelExpanded(true)
+            setPulsing(false)
+
+            // WHEN we temporarily allow section changes for this notification entry
+            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.uptimeMillis())
+
+            // THEN the notification list is invalidated
+            verifyStabilityManagerWasInvalidated(times(1))
+        }
+
+    @Test
+    fun testTemporarilyAllowSectionChanges_noInvalidationCalled() =
+        testScope.runTest {
+            // GIVEN section changes typically WOULD be allowed
+            setFullyDozed(true)
+            setSleepy(true)
+            setPanelExpanded(false)
+            setPulsing(false)
+
+            // WHEN we temporarily allow section changes for this notification entry
+            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+
+            // THEN invalidate is not called because this entry was never suppressed from reordering
+            verifyStabilityManagerWasInvalidated(never())
+        }
+
+    @Test
+    fun testTemporarilyAllowSectionChangesTimeout() =
+        testScope.runTest {
+            // GIVEN section changes typically WOULD be allowed
+            setFullyDozed(true)
+            setSleepy(true)
+            setPanelExpanded(false)
+            setPulsing(false)
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+
+            // WHEN we temporarily allow section changes for this notification entry
+            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+
+            // THEN invalidate is not called because this entry was never suppressed from
+            // reordering;
+            // THEN section changes are allowed for this notification
+            verifyStabilityManagerWasInvalidated(never())
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+
+            // WHEN we're pulsing (now disallowing reordering)
+            setPulsing(true)
+
+            // THEN we're still allowed to reorder this section because it's still in the list of
+            // notifications to allow section changes
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+
+            // WHEN the timeout for the temporarily allow section reordering runnable is finsihed
+            fakeBackgroundExecutor.advanceClockToNext()
+            fakeBackgroundExecutor.runNextReady()
+
+            // THEN section changes aren't allowed anymore
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+        }
+
+    @Test
+    fun testTemporarilyAllowSectionChanges_isPulsingChangeBeforeTimeout() =
+        testScope.runTest {
+            // GIVEN section changes typically wouldn't be allowed because the device is pulsing
+            setFullyDozed(true)
+            setSleepy(true)
+            setPanelExpanded(false)
+            setPulsing(true)
+
+            // WHEN we temporarily allow section changes for this notification entry
+            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+            // can now reorder, so invalidates
+            verifyStabilityManagerWasInvalidated(times(1))
+
+            // WHEN reordering is now allowed because device isn't pulsing anymore
+            setPulsing(false)
+
+            // THEN invalidate isn't called a second time since reordering was already allowed
+            verifyStabilityManagerWasInvalidated(times(1))
+        }
+
+    @Test
+    fun testMovingVisibleHeadsUpNotAllowed() =
+        testScope.runTest {
+            // GIVEN stability enforcing conditions
+            setPanelExpanded(true)
+            setSleepy(false)
+
+            // WHEN a notification is alerting and visible
+            headsUpRepository.setHeadsUpKeys(entry.key)
+            whenever(visibilityLocationProvider.isInVisibleLocation(any())).thenReturn(true)
+
+            // THEN the notification cannot be reordered
+            assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isFalse()
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+        }
+
+    @Test
+    fun testMovingInvisibleHeadsUpAllowed() =
+        testScope.runTest {
+            // GIVEN stability enforcing conditions
+            setPanelExpanded(true)
+            setSleepy(false)
+
+            // WHEN a notification is alerting but not visible
+            headsUpRepository.setHeadsUpKeys(entry.key)
+            whenever(visibilityLocationProvider.isInVisibleLocation(any())).thenReturn(false)
+
+            // THEN the notification can be reordered
+            assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isTrue()
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+        }
+
+    @Test
+    fun testNeverSuppressedChanges_noInvalidationCalled() =
+        testScope.runTest {
+            // GIVEN no notifications are currently being suppressed from grouping nor being sorted
+
+            // WHEN device isn't pulsing anymore
+
+            setPulsing(false)
+
+            // WHEN fully dozed
+            setFullyDozed(true)
+
+            // WHEN sleepy
+            setSleepy(true)
+
+            // WHEN panel isn't expanded
+            setPanelExpanded(false)
+
+            // THEN we never see any calls to invalidate since there weren't any notifications that
+            // were being suppressed from grouping or section changes
+            verifyStabilityManagerWasInvalidated(never())
+        }
+
+    @Test
+    fun testNotSuppressingGroupChangesAnymore_invalidationCalled() =
+        testScope.runTest {
+            // GIVEN visual stability is being maintained b/c panel is expanded
+            setPulsing(false)
+            setFullyDozed(false)
+            setSleepy(false)
+            setPanelExpanded(true)
+
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+            // WHEN the panel isn't expanded anymore
+            setPanelExpanded(false)
+
+            // THEN invalidate is called because we were previously suppressing a group change
+            verifyStabilityManagerWasInvalidated(times(1))
+        }
+
+    @Test
+    fun testNotLaunchingActivityAnymore_invalidationCalled() {
+        // GIVEN visual stability is being maintained b/c animation is playing
+        setActivityLaunching(true)
+
+        assertThat(notifStabilityManager.isPipelineRunAllowed()).isFalse()
+
+        // WHEN the animation has stopped playing
+        setActivityLaunching(false)
+
+        // THEN invalidate is called, b/c we were previously suppressing the pipeline from running
+        verifyStabilityManagerWasInvalidated(times(1))
+    }
+
+    @Test
+    fun testNotCollapsingPanelAnymore_invalidationCalled() {
+        // GIVEN visual stability is being maintained b/c animation is playing
+        setPanelCollapsing(true)
+
+        assertThat(notifStabilityManager.isPipelineRunAllowed()).isFalse()
+
+        // WHEN the animation has stopped playing
+        setPanelCollapsing(false)
+
+        // THEN invalidate is called, b/c we were previously suppressing the pipeline from running
+        verifyStabilityManagerWasInvalidated(times(1))
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_CHECK_LOCKSCREEN_GONE_TRANSITION)
+    @DisableSceneContainer
+    fun testNotLockscreenInGoneTransitionLegacy_invalidationCalled() {
+        // GIVEN visual stability is being maintained b/c animation is playing
+        whenever(keyguardStateController.isKeyguardFadingAway).thenReturn(true)
+        underTest.mKeyguardFadeAwayAnimationCallback.onKeyguardFadingAwayChanged()
+
+        assertThat(notifStabilityManager.isPipelineRunAllowed()).isFalse()
+
+        // WHEN the animation has stopped playing
+        whenever(keyguardStateController.isKeyguardFadingAway).thenReturn(false)
+        underTest.mKeyguardFadeAwayAnimationCallback.onKeyguardFadingAwayChanged()
+
+        // THEN invalidate is called, b/c we were previously suppressing the pipeline from running
+        verifyStabilityManagerWasInvalidated(times(1))
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_CHECK_LOCKSCREEN_GONE_TRANSITION)
+    @EnableSceneContainer
+    @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
+    fun testNotLockscreenInGoneTransition_invalidationCalled() =
+        testScope.runTest {
+            // GIVEN visual stability is being maintained b/c animation is playing
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    KeyguardState.LOCKSCREEN,
+                    KeyguardState.GONE,
+                    1f,
+                    TransitionState.RUNNING,
+                ),
+                /* validateStep = */ false,
+            )
+            testScope.testScheduler.runCurrent()
+            assertThat(notifStabilityManager.isPipelineRunAllowed()).isFalse()
+
+            // WHEN the animation has stopped playing
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    KeyguardState.LOCKSCREEN,
+                    KeyguardState.GONE,
+                    1f,
+                    TransitionState.FINISHED,
+                ),
+                /* validateStep = */ false,
+            )
+            testScope.testScheduler.runCurrent()
+
+            // THEN invalidate is called, b/c we were previously suppressing the pipeline from
+            // running
+            verifyStabilityManagerWasInvalidated(times(1))
+        }
+
+    @Test
+    fun testNeverSuppressPipelineRunFromPanelCollapse_noInvalidationCalled() {
+        // GIVEN animation is playing
+        setPanelCollapsing(true)
+
+        // WHEN the animation has stopped playing
+        setPanelCollapsing(false)
+
+        // THEN invalidate is not called, b/c nothing has been suppressed
+        verifyStabilityManagerWasInvalidated(never())
+    }
+
+    @Test
+    fun testNeverSuppressPipelineRunFromLaunchActivity_noInvalidationCalled() {
+        // GIVEN animation is playing
+        setActivityLaunching(true)
+
+        // WHEN the animation has stopped playing
+        setActivityLaunching(false)
+
+        // THEN invalidate is not called, b/c nothing has been suppressed
+        verifyStabilityManagerWasInvalidated(never())
+    }
+
+    @Test
+    fun testNotSuppressingEntryReorderingAnymoreWillInvalidate() =
+        testScope.runTest {
+            // GIVEN visual stability is being maintained b/c panel is expanded
+            setPulsing(false)
+            setFullyDozed(false)
+            setSleepy(false)
+            setPanelExpanded(true)
+            setCommunalShowing(false)
+
+            assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isFalse()
+            // The pipeline still has to report back that entry reordering was suppressed
+            notifStabilityManager.onEntryReorderSuppressed()
+
+            // WHEN the panel isn't expanded anymore
+            setPanelExpanded(false)
+
+            // THEN invalidate is called because we were previously suppressing an entry reorder
+            verifyStabilityManagerWasInvalidated(times(1))
+        }
+
+    @Test
+    @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
+    fun testCommunalShowingWillNotSuppressReordering() =
+        testScope.runTest {
+            // GIVEN panel is expanded, communal is showing, and QS is collapsed
+            setPulsing(false)
+            setFullyDozed(false)
+            setSleepy(false)
+            setPanelExpanded(true)
+            setQsExpanded(false)
+            setCommunalShowing(true)
+
+            // THEN Reordering should be allowed
+            assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isTrue()
+        }
+
+    @Test
+    fun testQsExpandedOverCommunalWillSuppressReordering() =
+        testScope.runTest {
+            // GIVEN panel is expanded and communal is showing, but QS is expanded
+            setPulsing(false)
+            setFullyDozed(false)
+            setSleepy(false)
+            setPanelExpanded(true)
+            setQsExpanded(true)
+            setCommunalShowing(true)
+
+            // THEN Reordering should not be allowed
+            assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isFalse()
+        }
+
+    @Test
+    fun testQueryingEntryReorderingButNotReportingReorderSuppressedDoesNotInvalidate() =
+        testScope.runTest {
+            // GIVEN visual stability is being maintained b/c panel is expanded
+            setPulsing(false)
+            setFullyDozed(false)
+            setSleepy(false)
+            setPanelExpanded(true)
+
+            assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isFalse()
+
+            // WHEN the panel isn't expanded anymore
+            setPanelExpanded(false)
+
+            // THEN invalidate is not called because we were not told that an entry reorder was
+            // suppressed
+            verifyStabilityManagerWasInvalidated(never())
+        }
+
+    @Test
+    fun testHeadsUp_allowedToChangeGroupAndSection() =
+        testScope.runTest {
+            // GIVEN group + section changes disallowed
+            setFullyDozed(false)
+            setSleepy(false)
+            setPanelExpanded(true)
+            setPulsing(true)
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+
+            // GIVEN mEntry is a HUN
+            headsUpRepository.setHeadsUpKeys(entry.key)
+
+            // THEN group + section changes are allowed
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isTrue()
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+
+            // BUT pruning the group for which this is the summary would still NOT be allowed.
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+        }
+
+    @Test
+    fun everyChangeAllowed_onReorderingEnabled_legacy() =
+        testScope.runTest {
+            assumeFalse(StabilizeHeadsUpGroup.isEnabled)
+            // GIVEN - reordering is allowed.
+            setPulsing(false)
+            setPanelExpanded(false)
+
+            // THEN
+            assertThat(notifStabilityManager.isEveryChangeAllowed()).isTrue()
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isTrue()
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isTrue()
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+            assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isTrue()
+        }
+
+    @Test
+    fun everyChangeAllowed_noActiveHeadsUpGroup_onReorderingEnabled() =
+        testScope.runTest {
+            assumeTrue(StabilizeHeadsUpGroup.isEnabled)
+            // GIVEN - reordering is allowed.
+            setPulsing(false)
+            setPanelExpanded(false)
+
+            // GIVEN - empty heads-up-group keys
+            underTest.setHeadsUpGroupKeys(setOf())
+
+            // THEN
+            assertThat(notifStabilityManager.isEveryChangeAllowed()).isTrue()
+            assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isTrue()
+            assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isTrue()
+            assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+            assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isTrue()
+        }
+
+    @Test
+    fun everyChangeDisallowed_activeHeadsUpGroup_onReorderingEnabled() =
+        testScope.runTest {
+            assumeTrue(StabilizeHeadsUpGroup.isEnabled)
+            // GIVEN - reordering is allowed.
+            setPulsing(false)
+            setPanelExpanded(false)
+
+            // GIVEN - there is a group heads-up.
+            underTest.setHeadsUpGroupKeys(setOf("heads_up_group_key"))
+
+            // THEN
+            assertThat(notifStabilityManager.isEveryChangeAllowed()).isFalse()
+        }
+
+    @Test
+    fun nonHeadsUpGroup_changesAllowed_onReorderingEnabled() =
+        testScope.runTest {
+            assumeTrue(StabilizeHeadsUpGroup.isEnabled)
+            // GIVEN - reordering is allowed.
+            setPulsing(false)
+            setPanelExpanded(false)
+
+            //  GIVEN - there is a group heads-up.
+            val headsUpGroupKey = "heads_up_group_key"
+            underTest.setHeadsUpGroupKeys(setOf(headsUpGroupKey))
+            headsUpRepository.setHeadsUpKeys(headsUpGroupKey)
+
+            // GIVEN - HUN Group Summary
+            val nonHeadsUpGroupSummary: NotificationEntry = mock()
+            whenever(nonHeadsUpGroupSummary.key).thenReturn("non_heads_up_group_key")
+            whenever(nonHeadsUpGroupSummary.isSummaryWithChildren).thenReturn(true)
+            val nonHeadsUpGroupEntry: GroupEntry = mock()
+            whenever(nonHeadsUpGroupEntry.summary).thenReturn(nonHeadsUpGroupSummary)
+            whenever(nonHeadsUpGroupEntry.representativeEntry).thenReturn(nonHeadsUpGroupSummary)
+
+            // THEN
+            assertThat(notifStabilityManager.isGroupPruneAllowed(nonHeadsUpGroupEntry)).isTrue()
+            assertThat(notifStabilityManager.isEntryReorderingAllowed(nonHeadsUpGroupEntry))
+                .isTrue()
+        }
+
+    @Test
+    fun headsUpGroup_changesDisallowed_onReorderingEnabled() =
+        testScope.runTest {
+            assumeTrue(StabilizeHeadsUpGroup.isEnabled)
+            // GIVEN - reordering is allowed.
+            setPulsing(false)
+            setPanelExpanded(false)
+
+            //  GIVEN - there is a group heads-up.
+            val headsUpGroupKey = "heads_up_group_key"
+            underTest.setHeadsUpGroupKeys(setOf(headsUpGroupKey))
+            headsUpRepository.setHeadsUpKeys(headsUpGroupKey)
+
+            // GIVEN - HUN Group
+            val headsUpGroupSummary: NotificationEntry = mock()
+            whenever(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false)
+            whenever(headsUpGroupSummary.key).thenReturn(headsUpGroupKey)
+            whenever(headsUpGroupSummary.isSummaryWithChildren).thenReturn(true)
+
+            val headsUpGroupEntry: GroupEntry = mock()
+            whenever(headsUpGroupEntry.summary).thenReturn(headsUpGroupSummary)
+            whenever(headsUpGroupEntry.representativeEntry).thenReturn(headsUpGroupSummary)
+
+            whenever(headsUpGroupSummary.parent).thenReturn(headsUpGroupEntry)
+
+            // GIVEN - HUN is in visible location
+            whenever(visibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary))
+                .thenReturn(true)
+
+            // THEN
+            assertThat(notifStabilityManager.isGroupPruneAllowed(headsUpGroupEntry)).isFalse()
+            assertThat(notifStabilityManager.isEntryReorderingAllowed(headsUpGroupEntry)).isFalse()
+        }
+
+    @Test
+    fun headsUpGroupSummaries_changesDisallowed_onReorderingEnabled() =
+        testScope.runTest {
+            assumeTrue(StabilizeHeadsUpGroup.isEnabled)
+            // GIVEN - reordering is allowed.
+            setPulsing(false)
+            setPanelExpanded(false)
+
+            //  GIVEN - there is a group heads-up.
+            val headsUpGroupKey = "heads_up_group_key"
+            underTest.setHeadsUpGroupKeys(setOf(headsUpGroupKey))
+            headsUpRepository.setHeadsUpKeys(headsUpGroupKey)
+
+            // GIVEN - HUN Group
+            val headsUpGroupSummary: NotificationEntry = mock()
+            whenever(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false)
+            whenever(headsUpGroupSummary.key).thenReturn(headsUpGroupKey)
+            whenever(headsUpGroupSummary.isSummaryWithChildren).thenReturn(true)
+
+            val headsUpGroupEntry: GroupEntry = mock()
+            whenever(headsUpGroupEntry.summary).thenReturn(headsUpGroupSummary)
+            whenever(headsUpGroupEntry.representativeEntry).thenReturn(headsUpGroupSummary)
+
+            whenever(headsUpGroupSummary.parent).thenReturn(headsUpGroupEntry)
+
+            // GIVEN - HUN is in visible location
+            whenever(visibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary))
+                .thenReturn(true)
+
+            // THEN
+            assertThat(notifStabilityManager.isGroupChangeAllowed(headsUpGroupSummary)).isFalse()
+            assertThat(notifStabilityManager.isEntryReorderingAllowed(headsUpGroupSummary))
+                .isFalse()
+            assertThat(notifStabilityManager.isSectionChangeAllowed(headsUpGroupSummary)).isFalse()
+        }
+
+    @Test
+    fun notificationInNonHUNGroup_changesAllowed_onReorderingEnabled() =
+        testScope.runTest {
+            assumeTrue(StabilizeHeadsUpGroup.isEnabled)
+            // GIVEN - reordering is allowed.
+            setPulsing(false)
+            setPanelExpanded(false)
+
+            //  GIVEN - there is a group heads-up.
+            val headsUpGroupKey = "heads_up_group_key"
+            underTest.setHeadsUpGroupKeys(setOf(headsUpGroupKey))
+            headsUpRepository.setHeadsUpKeys(headsUpGroupKey)
+
+            // GIVEN - non HUN parent Group Summary
+            val groupSummary: NotificationEntry = mock()
+            whenever(groupSummary.key).thenReturn("non_heads_up_group_key")
+            whenever(groupSummary.isSummaryWithChildren).thenReturn(true)
+
+            val nonHeadsUpGroupEntry: GroupEntry = mock()
+            whenever(nonHeadsUpGroupEntry.summary).thenReturn(groupSummary)
+            whenever(nonHeadsUpGroupEntry.representativeEntry).thenReturn(groupSummary)
+
+            // GIVEN - child entry in a non heads-up group.
+            val childEntry: NotificationEntry = mock()
+            whenever(childEntry.rowIsChildInGroup()).thenReturn(true)
+            whenever(childEntry.parent).thenReturn(nonHeadsUpGroupEntry)
+            whenever(childEntry.parent).thenReturn(nonHeadsUpGroupEntry)
+
+            // THEN
+            assertThat(notifStabilityManager.isGroupChangeAllowed(childEntry)).isTrue()
+            assertThat(notifStabilityManager.isSectionChangeAllowed(childEntry)).isTrue()
+            assertThat(notifStabilityManager.isEntryReorderingAllowed(nonHeadsUpGroupEntry))
+                .isTrue()
+        }
+
+    @Test
+    fun notificationInHUNGroup_changesDisallowed_reorderingEnabled() =
+        testScope.runTest {
+            assumeTrue(StabilizeHeadsUpGroup.isEnabled)
+            // GIVEN - reordering is allowed.
+            setPulsing(false)
+            setPanelExpanded(false)
+
+            // GIVEN - there is a group heads-up.
+            val headsUpGroupKey = "heads_up_group_key"
+            underTest.setHeadsUpGroupKeys(setOf(headsUpGroupKey))
+            headsUpRepository.setHeadsUpKeys(headsUpGroupKey)
+
+            // GIVEN - HUN Group Summary
+            val headsUpGroupSummary: NotificationEntry = mock()
+            whenever(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false)
+            whenever(headsUpGroupSummary.key).thenReturn(headsUpGroupKey)
+            whenever(headsUpGroupSummary.isSummaryWithChildren).thenReturn(true)
+
+            val nonHeadsUpGroupEntry: GroupEntry = mock()
+            whenever(nonHeadsUpGroupEntry.summary).thenReturn(headsUpGroupSummary)
+            whenever(nonHeadsUpGroupEntry.representativeEntry).thenReturn(headsUpGroupSummary)
+
+            // GIVEN - child entry in a non heads-up group.
+            val childEntry: NotificationEntry =
+                mock<NotificationEntry>().apply { whenever(key).thenReturn("child") }
+            whenever(childEntry.rowIsChildInGroup()).thenReturn(true)
+            whenever(childEntry.parent).thenReturn(nonHeadsUpGroupEntry)
+
+            // GIVEN - HUN is in visible location
+            whenever(visibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary))
+                .thenReturn(true)
+
+            // THEN
+            assertThat(notifStabilityManager.isGroupChangeAllowed(childEntry)).isFalse()
+            assertThat(notifStabilityManager.isSectionChangeAllowed(childEntry)).isFalse()
+            assertThat(notifStabilityManager.isEntryReorderingAllowed(childEntry)).isFalse()
+        }
+
+    private fun verifyStabilityManagerWasInvalidated(mode: VerificationMode) {
+        verify(invalidateListener, mode).onPluggableInvalidated(eq(notifStabilityManager), any())
+    }
+
+    private fun setActivityLaunching(activityLaunching: Boolean) {
+        kosmos.shadeAnimationInteractor.setIsLaunchingActivity(activityLaunching)
+        testScope.testScheduler.runCurrent()
+    }
+
+    private fun setPanelCollapsing(collapsing: Boolean) {
+        shadeRepository.setLegacyIsClosing(collapsing)
+        testScope.testScheduler.runCurrent()
+    }
+
+    private fun setCommunalShowing(isShowing: Boolean) {
+        val showingFlow =
+            MutableStateFlow<ObservableTransitionState>(
+                ObservableTransitionState.Idle(
+                    if (isShowing) CommunalScenes.Communal else CommunalScenes.Blank
+                )
+            )
+        kosmos.communalSceneInteractor.setTransitionState(showingFlow)
+        testScope.testScheduler.runCurrent()
+    }
+
+    private fun setQsExpanded(isExpanded: Boolean) {
+        kosmos.shadeRepository.setQsExpansion(if (isExpanded) 1.0f else 0.0f)
+        testScope.testScheduler.runCurrent()
+    }
+
+    private fun setPulsing(pulsing: Boolean) = statusBarStateListener.onPulsingChanged(pulsing)
+
+    private fun setFullyDozed(fullyDozed: Boolean) {
+        val dozeAmount = if (fullyDozed) 1f else 0f
+        statusBarStateListener.onDozeAmountChanged(dozeAmount, dozeAmount)
+    }
+
+    private fun setSleepy(sleepy: Boolean) {
+        if (sleepy) {
+            wakefulnessObserver.onFinishedGoingToSleep()
+        } else {
+            wakefulnessObserver.onStartedWakingUp()
+        }
+    }
+
+    private suspend fun setPanelExpanded(expanded: Boolean) =
+        setPanelExpandedAndLockscreenShowing(expanded, /* lockscreenShowing= */ 0.0f)
+
+    private suspend fun setLockscreenShowing(lockscreenShowing: Float) =
+        setPanelExpandedAndLockscreenShowing(/* panelExpanded= */ false, lockscreenShowing)
+
+    private suspend fun setPanelExpandedAndLockscreenShowing(
+        panelExpanded: Boolean,
+        lockscreenShowing: Float,
+    ) {
+        if (SceneContainerFlag.isEnabled) {
+            statusBarStateListener.onExpandedChanged(panelExpanded)
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(
+                makeLockscreenTransitionStep(lockscreenShowing),
+                /* validateStep = */ false,
+            )
+        } else {
+            statusBarStateListener.onExpandedChanged(panelExpanded || lockscreenShowing > 0.0f)
+        }
+    }
+
+    private fun makeLockscreenTransitionStep(value: Float): TransitionStep {
+        return when (value) {
+            0.0f -> TransitionStep(KeyguardState.GONE)
+            1.0f -> TransitionStep(KeyguardState.LOCKSCREEN)
+            else ->
+                TransitionStep(
+                    KeyguardState.GONE,
+                    KeyguardState.LOCKSCREEN,
+                    value,
+                    TransitionState.RUNNING,
+                )
+        }
+    }
+}
+
+private fun FakeHeadsUpNotificationRepository.setHeadsUpKeys(vararg keys: String) {
+    setNotifications(keys.map { FakeHeadsUpRowRepository(key = it) })
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimatorTest.kt
new file mode 100644
index 0000000..206eb89
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimatorTest.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.headsup
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.Before
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+class HeadsUpAnimatorTest : SysuiTestCase() {
+    @Before
+    fun setUp() {
+        context.getOrCreateTestableResources().apply {
+            this.addOverride(R.dimen.heads_up_appear_y_above_screen, TEST_Y_ABOVE_SCREEN)
+        }
+    }
+
+    @Test
+    fun getHeadsUpYTranslation_fromBottomTrue_usesBottomAndYAbove() {
+        val underTest = HeadsUpAnimator(context)
+        underTest.stackTopMargin = 30
+        underTest.headsUpAppearHeightBottom = 300
+
+        val yTranslation = underTest.getHeadsUpYTranslation(isHeadsUpFromBottom = true)
+
+        assertThat(yTranslation).isEqualTo(TEST_Y_ABOVE_SCREEN + 300)
+    }
+
+    @Test
+    fun getHeadsUpYTranslation_fromBottomFalse_usesTopMarginAndYAbove() {
+        val underTest = HeadsUpAnimator(context)
+        underTest.stackTopMargin = 30
+        underTest.headsUpAppearHeightBottom = 300
+
+        val yTranslation = underTest.getHeadsUpYTranslation(isHeadsUpFromBottom = false)
+
+        assertThat(yTranslation).isEqualTo(-30 - TEST_Y_ABOVE_SCREEN)
+    }
+
+    @Test
+    fun getHeadsUpYTranslation_resourcesUpdated() {
+        val underTest = HeadsUpAnimator(context)
+        underTest.stackTopMargin = 30
+        underTest.headsUpAppearHeightBottom = 300
+
+        val yTranslation = underTest.getHeadsUpYTranslation(isHeadsUpFromBottom = true)
+
+        assertThat(yTranslation).isEqualTo(TEST_Y_ABOVE_SCREEN + 300)
+
+        // WHEN the resource is updated
+        val newYAbove = 600
+        context.getOrCreateTestableResources().apply {
+            this.addOverride(R.dimen.heads_up_appear_y_above_screen, newYAbove)
+        }
+        underTest.updateResources(context)
+
+        // THEN HeadsUpAnimator knows about it
+        assertThat(underTest.getHeadsUpYTranslation(isHeadsUpFromBottom = true))
+            .isEqualTo(newYAbove + 300)
+    }
+
+    companion object {
+        private const val TEST_Y_ABOVE_SCREEN = 50
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index 25ae13f..f060cae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -21,6 +21,7 @@
 import android.net.Uri
 import android.os.UserHandle
 import android.os.UserHandle.USER_ALL
+import android.service.notification.StatusBarNotification
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -36,6 +37,7 @@
 import com.android.systemui.statusbar.SbnBuilder
 import com.android.systemui.statusbar.SmartReplyController
 import com.android.systemui.statusbar.notification.ColorUpdateLogger
+import com.android.systemui.statusbar.notification.collection.EntryAdapter
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider
@@ -229,6 +231,10 @@
     @Test
     fun registerSettingsListener_forBubbles() {
         controller.init(mock(NotificationEntry::class.java))
+        val entryAdapter = mock(EntryAdapter::class.java)
+        whenever(entryAdapter.sbn).thenReturn(mock(StatusBarNotification::class.java))
+        whenever(view.entryAdapter).thenReturn(entryAdapter)
+
         val viewStateObserver = withArgCaptor {
             verify(view).addOnAttachStateChangeListener(capture())
         }
@@ -239,6 +245,9 @@
     @Test
     fun unregisterSettingsListener_forBubbles() {
         controller.init(mock(NotificationEntry::class.java))
+        val entryAdapter = mock(EntryAdapter::class.java)
+        whenever(entryAdapter.sbn).thenReturn(mock(StatusBarNotification::class.java))
+        whenever(view.entryAdapter).thenReturn(entryAdapter)
         val viewStateObserver = withArgCaptor {
             verify(view).addOnAttachStateChangeListener(capture())
         }
@@ -255,6 +264,7 @@
 
     @Test
     fun settingsListener_invalidUserId() {
+        whenever(view.entryAdapter).thenReturn(mock(EntryAdapter::class.java))
         controller.mSettingsListener.onSettingChanged(BUBBLES_SETTING_URI, -1000, "1")
         controller.mSettingsListener.onSettingChanged(BUBBLES_SETTING_URI, -1000, null)
 
@@ -265,6 +275,12 @@
     fun settingsListener_validUserId() {
         val childView: NotificationContentView = mock()
         whenever(view.privateLayout).thenReturn(childView)
+        val entryAdapter = mock(EntryAdapter::class.java)
+        val sbn =
+            SbnBuilder().setNotification(Notification.Builder(mContext).build())
+                .setUser(UserHandle.of(view.entry.sbn.userId)).build()
+        whenever(entryAdapter.sbn).thenReturn(sbn)
+        whenever(view.entryAdapter).thenReturn(entryAdapter)
 
         controller.mSettingsListener.onSettingChanged(
             BUBBLES_SETTING_URI,
@@ -293,6 +309,9 @@
             .thenReturn(
                 NotificationEntryBuilder().setSbn(sbn).setUser(UserHandle.of(USER_ALL)).build()
             )
+        val entryAdapter = mock(EntryAdapter::class.java)
+        whenever(entryAdapter.sbn).thenReturn(sbn)
+        whenever(view.entryAdapter).thenReturn(entryAdapter)
 
         controller.mSettingsListener.onSettingChanged(BUBBLES_SETTING_URI, 9, "1")
         verify(childView).setBubblesEnabledForUser(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
index 979a1d0..fd49f60 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
@@ -79,6 +79,7 @@
         mRow = spy(mNotificationTestHelper.createRow());
         Notification notification = mRow.getEntry().getSbn().getNotification();
         notification.contentIntent = mock(PendingIntent.class);
+        when(notification.contentIntent.isActivity()).thenReturn(true);
         doReturn(true).when(mRow).startDragAndDrop(any(), any(), any(), anyInt());
         mGroupRow = mNotificationTestHelper.createGroup(4);
         when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 92ceb60..c376fad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -70,6 +70,8 @@
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -106,6 +108,7 @@
 
 import org.mockito.ArgumentCaptor;
 
+import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -338,6 +341,46 @@
     }
 
     /**
+     * Returns an {@link GroupEntry} group with the given number of child
+     * notifications.
+     */
+    public GroupEntry createGroupEntry(int numChildren,
+            @Nullable List<NotificationEntry> additionalChildren) {
+        Notification summary = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setGroupSummary(true)
+                .setGroup(GROUP_KEY)
+                .build();
+
+        NotificationEntry summaryEntry = new NotificationEntryBuilder()
+                .setPkg(PKG)
+                .setOpPkg(PKG)
+                .setId(mId++)
+                .setUid(UID)
+                .setInitialPid(2000)
+                .setNotification(summary)
+                .setParent(GroupEntry.ROOT_ENTRY)
+                .build();
+        GroupEntryBuilder groupEntry = new GroupEntryBuilder()
+                .setSummary(summaryEntry);
+
+        for (int i = 0; i < numChildren; i++) {
+            NotificationEntry child = new NotificationEntryBuilder()
+                    .setParent(GroupEntry.ROOT_ENTRY)
+                    .setNotification(new Notification.Builder(mContext, "")
+                            .setSmallIcon(R.drawable.ic_person)
+                            .setGroup(GROUP_KEY)
+                            .build())
+                    .build();
+            groupEntry.addChild(child);
+        }
+        for (NotificationEntry entry : additionalChildren) {
+            groupEntry.addChild(entry);
+        }
+        return groupEntry.build();
+    }
+
+    /**
      * Returns an {@link ExpandableNotificationRow} group with the given number of child
      * notifications.
      */
@@ -411,6 +454,23 @@
     }
 
     /**
+     * Returns an {@link NotificationEntry} that should be shown as a bubble and is part
+     * of a group of notifications.
+     */
+    public NotificationEntry createBubbleEntryInGroup() throws Exception {
+        Notification n = createNotification(false /* isGroupSummary */,
+                GROUP_KEY /* groupKey */,
+                makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */));
+        n.flags |= FLAG_BUBBLE;
+        NotificationEntry entry = generateEntry(n, PKG, UID, USER_HANDLE,
+                mDefaultInflationFlags, IMPORTANCE_HIGH);
+        modifyRanking(entry)
+                .setCanBubble(true)
+                .build();
+        return entry;
+    }
+
+    /**
      * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble and is part
      * of a group of notifications.
      */
@@ -573,6 +633,41 @@
         return mKeyguardBypassController;
     }
 
+    private NotificationEntry generateEntry(
+            Notification notification,
+            String pkg,
+            int uid,
+            UserHandle userHandle,
+            @InflationFlag int extraInflationFlags,
+            int importance)
+            throws Exception {
+        final NotificationChannel channel =
+                new NotificationChannel(
+                        notification.getChannelId(),
+                        notification.getChannelId(),
+                        importance);
+        channel.setBlockable(true);
+
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setPkg(pkg)
+                .setOpPkg(pkg)
+                .setId(mId++)
+                .setUid(uid)
+                .setInitialPid(2000)
+                .setNotification(notification)
+                .setUser(userHandle)
+                .setPostTime(System.currentTimeMillis())
+                .setChannel(channel)
+                .updateRanking(rankingBuilder -> rankingBuilder.setIsConversation(
+                        notification.isStyle(Notification.MessagingStyle.class)
+                ))
+                .build();
+
+
+        return entry;
+    }
+
+
     private ExpandableNotificationRow generateRow(
             Notification notification,
             String pkg,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index 4b8a0c2..f7bbf98 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.notification.collection.EntryAdapter
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded
@@ -436,6 +437,9 @@
         val row = mock(ExpandableNotificationRow::class.java)
         val entry = mock(NotificationEntry::class.java)
         whenever(entry.isStickyAndNotDemoted).thenReturn(isSticky)
+        val entryAdapter = mock(EntryAdapter::class.java)
+        whenever(entryAdapter.canPeek()).thenReturn(isSticky)
+        whenever(row.entryAdapter).thenReturn(entryAdapter)
         val sbn = mock(StatusBarNotification::class.java)
         whenever(entry.sbn).thenReturn(sbn)
         whenever(row.entry).thenReturn(entry)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index ce655ef..9545150 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView.FooterViewState
 import com.android.systemui.statusbar.notification.headsup.AvalancheController
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -55,7 +57,8 @@
     private val avalancheController = mock<AvalancheController>()
 
     private val hostView = FrameLayout(context)
-    private val stackScrollAlgorithm = StackScrollAlgorithm(context, hostView)
+    private lateinit var headsUpAnimator: HeadsUpAnimator
+    private lateinit var stackScrollAlgorithm: StackScrollAlgorithm
     private val notificationRow = mock<ExpandableNotificationRow>()
     private val notificationEntry = mock<NotificationEntry>()
     private val notificationEntryAdapter = mock<EntryAdapter>()
@@ -101,7 +104,10 @@
         @JvmStatic
         @Parameters(name = "{0}")
         fun getParams(): List<FlagsParameterization> {
-            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+            return FlagsParameterization.allCombinationsOf(
+                    NotificationsHunSharedAnimationValues.FLAG_NAME
+                )
+                .andSceneContainer()
         }
     }
 
@@ -123,6 +129,15 @@
         ambientState.isSmallScreen = true
 
         hostView.addView(notificationRow)
+
+        if (NotificationsHunSharedAnimationValues.isEnabled) {
+             headsUpAnimator = HeadsUpAnimator(context)
+        }
+        stackScrollAlgorithm = StackScrollAlgorithm(
+            context,
+            hostView,
+            if (::headsUpAnimator.isInitialized) headsUpAnimator else null,
+        )
     }
 
     private fun isTv(): Boolean {
@@ -403,7 +418,11 @@
         ambientState.setLayoutMinHeight(2500) // Mock the height of shade
         ambientState.stackY = 2500f // Scroll over the max translation
         stackScrollAlgorithm.setIsExpanded(true) // Mark the shade open
-        stackScrollAlgorithm.setHeadsUpAppearHeightBottom(bottomOfScreen.toInt())
+        if (NotificationsHunSharedAnimationValues.isEnabled) {
+            headsUpAnimator.headsUpAppearHeightBottom = bottomOfScreen.toInt()
+        } else {
+            stackScrollAlgorithm.setHeadsUpAppearHeightBottom(bottomOfScreen.toInt())
+        }
         whenever(notificationRow.mustStayOnScreen()).thenReturn(true)
         whenever(notificationRow.isHeadsUp).thenReturn(true)
         whenever(notificationRow.isAboveShelf).thenReturn(true)
@@ -419,6 +438,9 @@
         val topMargin = 100f
         ambientState.maxHeadsUpTranslation = 2000f
         ambientState.stackTopMargin = topMargin.toInt()
+        if (NotificationsHunSharedAnimationValues.isEnabled) {
+            headsUpAnimator.stackTopMargin = topMargin.toInt()
+        }
         whenever(notificationRow.intrinsicHeight).thenReturn(100)
         whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
index e7be20e..4a761917 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
@@ -16,12 +16,16 @@
 
 package com.android.systemui.statusbar.notification.stack
 
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.AnimatorTestRule
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR
@@ -49,10 +53,10 @@
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
 class StackStateAnimatorTest : SysuiTestCase() {
-
     @get:Rule val animatorTestRule = AnimatorTestRule(this)
 
     private lateinit var stackStateAnimator: StackStateAnimator
+    private lateinit var headsUpAnimator: HeadsUpAnimator
     private val stackScroller: NotificationStackScrollLayout = mock()
     private val view: ExpandableView = mock()
     private val viewState: ExpandableViewState =
@@ -69,11 +73,20 @@
 
         whenever(stackScroller.context).thenReturn(context)
         whenever(view.viewState).thenReturn(viewState)
-        stackStateAnimator = StackStateAnimator(mContext, stackScroller)
+
+        if (NotificationsHunSharedAnimationValues.isEnabled) {
+            headsUpAnimator = HeadsUpAnimator(context)
+        }
+        stackStateAnimator = StackStateAnimator(
+            mContext,
+            stackScroller,
+            if (::headsUpAnimator.isInitialized) headsUpAnimator else null,
+        )
     }
 
     @Test
-    fun startAnimationForEvents_headsUpFromTop_startsHeadsUpAppearAnim() {
+    @DisableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+    fun startAnimationForEvents_headsUpFromTop_startsHeadsUpAppearAnim_flagOff() {
         val topMargin = 50f
         val expectedStartY = -topMargin - stackStateAnimator.mHeadsUpAppearStartAboveScreen
         val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR)
@@ -94,7 +107,30 @@
     }
 
     @Test
-    fun startAnimationForEvents_headsUpFromBottom_startsHeadsUpAppearAnim() {
+    @EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+    fun startAnimationForEvents_headsUpFromTop_startsHeadsUpAppearAnim_flagOn() {
+        val topMargin = 50f
+        val expectedStartY = -topMargin - HEADS_UP_ABOVE_SCREEN
+        val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR)
+        headsUpAnimator.stackTopMargin = topMargin.toInt()
+
+        stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
+
+        verify(view).setFinalActualHeight(VIEW_HEIGHT)
+        verify(view, description("should animate from the top")).translationY = expectedStartY
+        verify(view)
+            .performAddAnimation(
+                /* delay= */ 0L,
+                /* duration= */ ANIMATION_DURATION_HEADS_UP_APPEAR.toLong(),
+                /* isHeadsUpAppear= */ true,
+                /* isHeadsUpCycling= */ false,
+                /* onEndRunnable= */ null,
+            )
+    }
+
+    @Test
+    @DisableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+    fun startAnimationForEvents_headsUpFromBottom_startsHeadsUpAppearAnim_flagOff() {
         val screenHeight = 2000f
         val expectedStartY = screenHeight + stackStateAnimator.mHeadsUpAppearStartAboveScreen
         val event =
@@ -118,7 +154,63 @@
     }
 
     @Test
-    fun startAnimationForEvents_startsHeadsUpDisappearAnim() {
+    @EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+    fun startAnimationForEvents_headsUpFromBottom_startsHeadsUpAppearAnim_flagOn() {
+        val screenHeight = 2000f
+        val expectedStartY = screenHeight + HEADS_UP_ABOVE_SCREEN
+        val event =
+            AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR).apply {
+                headsUpFromBottom = true
+            }
+        headsUpAnimator.headsUpAppearHeightBottom = screenHeight.toInt()
+
+        stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
+
+        verify(view).setFinalActualHeight(VIEW_HEIGHT)
+        verify(view, description("should animate from the bottom")).translationY = expectedStartY
+        verify(view)
+            .performAddAnimation(
+                /* delay= */ 0L,
+                /* duration= */ ANIMATION_DURATION_HEADS_UP_APPEAR.toLong(),
+                /* isHeadsUpAppear= */ true,
+                /* isHeadsUpCycling= */ false,
+                /* onEndRunnable= */ null,
+            )
+    }
+
+    @Test
+    @DisableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+    fun startAnimationForEvents_startsHeadsUpDisappearAnim_flagOff() {
+        val disappearDuration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR.toLong()
+        val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR)
+        clearInvocations(view)
+        stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
+
+        verify(view)
+            .performRemoveAnimation(
+                /* duration= */ eq(disappearDuration),
+                /* delay= */ eq(0L),
+                /* translationDirection= */ eq(0f),
+                /* isHeadsUpAnimation= */ eq(true),
+                /* isHeadsUpCycling= */ eq(false),
+                /* onStartedRunnable= */ any(),
+                /* onFinishedRunnable= */ runnableCaptor.capture(),
+                /* animationListener= */ any(),
+                /* clipSide= */ eq(ExpandableView.ClipSide.BOTTOM),
+            )
+
+        animatorTestRule.advanceTimeBy(disappearDuration) // move to the end of SSA animations
+        runnableCaptor.value.run() // execute the end runnable
+
+        verify(view, description("should be translated to the heads up appear start"))
+            .translationY = -stackStateAnimator.mHeadsUpAppearStartAboveScreen
+        verify(view, description("should be called at the end of the disappear animation"))
+            .removeFromTransientContainer()
+    }
+
+    @Test
+    @EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+    fun startAnimationForEvents_startsHeadsUpDisappearAnim_flagOn() {
         val disappearDuration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR.toLong()
         val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR)
         clearInvocations(view)
diff --git a/packages/SystemUI/pods/Android.bp b/packages/SystemUI/pods/Android.bp
index 1547ae2..fba8962 100644
--- a/packages/SystemUI/pods/Android.bp
+++ b/packages/SystemUI/pods/Android.bp
@@ -31,7 +31,7 @@
         warning_checks: ["MissingApacheLicenseDetector"],
     },
     kotlincflags: [
-        "-Xexplicit-api=warning",
+        "-Xexplicit-api=strict",
         "-Xjvm-default=all",
     ],
     defaults_visibility: [":__subpackages__"],
diff --git a/packages/SystemUI/pods/com/android/systemui/retail/RetailModeModule.kt b/packages/SystemUI/pods/com/android/systemui/retail/RetailModeModule.kt
index c20e368..fe78bb3 100644
--- a/packages/SystemUI/pods/com/android/systemui/retail/RetailModeModule.kt
+++ b/packages/SystemUI/pods/com/android/systemui/retail/RetailModeModule.kt
@@ -24,11 +24,15 @@
 import dagger.Module
 
 @Module
-abstract class RetailModeModule {
+public abstract class RetailModeModule {
 
     @Binds
-    abstract fun bindsRetailModeRepository(impl: RetailModeSettingsRepository): RetailModeRepository
+    public abstract fun bindsRetailModeRepository(
+        impl: RetailModeSettingsRepository
+    ): RetailModeRepository
 
     @Binds
-    abstract fun bindsRetailModeInteractor(impl: RetailModeInteractorImpl): RetailModeInteractor
+    public abstract fun bindsRetailModeInteractor(
+        impl: RetailModeInteractorImpl
+    ): RetailModeInteractor
 }
diff --git a/packages/SystemUI/pods/com/android/systemui/retail/data/repository/RetailModeRepository.kt b/packages/SystemUI/pods/com/android/systemui/retail/data/repository/RetailModeRepository.kt
index c9eac25..4fd6985 100644
--- a/packages/SystemUI/pods/com/android/systemui/retail/data/repository/RetailModeRepository.kt
+++ b/packages/SystemUI/pods/com/android/systemui/retail/data/repository/RetailModeRepository.kt
@@ -19,11 +19,11 @@
 import kotlinx.coroutines.flow.StateFlow
 
 /** Repository to track if the device is in Retail mode */
-interface RetailModeRepository {
+public interface RetailModeRepository {
     /** Flow of whether the device is currently in retail mode. */
-    val retailMode: StateFlow<Boolean>
+    public val retailMode: StateFlow<Boolean>
 
     /** Last value of whether the device is in retail mode. */
-    val inRetailMode: Boolean
+    public val inRetailMode: Boolean
         get() = retailMode.value
 }
diff --git a/packages/SystemUI/pods/com/android/systemui/retail/data/repository/impl/RetailModeSettingsRepository.kt b/packages/SystemUI/pods/com/android/systemui/retail/data/repository/impl/RetailModeSettingsRepository.kt
index 8955263..7296835 100644
--- a/packages/SystemUI/pods/com/android/systemui/retail/data/repository/impl/RetailModeSettingsRepository.kt
+++ b/packages/SystemUI/pods/com/android/systemui/retail/data/repository/impl/RetailModeSettingsRepository.kt
@@ -38,17 +38,17 @@
 /**
  * Tracks [Settings.Global.DEVICE_DEMO_MODE].
  *
- * @see UserManager.isDeviceInDemoMode
+ * @see android.os.UserManager.isDeviceInDemoMode
  */
 @SysUISingleton
-class RetailModeSettingsRepository
+public class RetailModeSettingsRepository
 @Inject
 constructor(
     globalSettings: GlobalSettings,
     @Background backgroundDispatcher: CoroutineDispatcher,
     @Application scope: CoroutineScope,
 ) : RetailModeRepository {
-    override val retailMode =
+    override val retailMode: StateFlow<Boolean> =
         conflatedCallbackFlow {
                 val observer =
                     object : ContentObserver(null) {
@@ -66,7 +66,7 @@
             .flowOn(backgroundDispatcher)
             .stateIn(scope, SharingStarted.Eagerly, false)
 
-    companion object {
+    public companion object {
         private const val RETAIL_MODE_SETTING = Settings.Global.DEVICE_DEMO_MODE
     }
 }
diff --git a/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/RetailModeInteractor.kt b/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/RetailModeInteractor.kt
index 748e34d..3e44f8d 100644
--- a/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/RetailModeInteractor.kt
+++ b/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/RetailModeInteractor.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.retail.domain.interactor
 
 /** Interactor to determine if the device is currently in retail mode */
-interface RetailModeInteractor {
+public interface RetailModeInteractor {
     /** Whether the device is currently in retail mode */
-    val isInRetailMode: Boolean
+    public val isInRetailMode: Boolean
 }
diff --git a/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/impl/RetailModeInteractorImpl.kt b/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/impl/RetailModeInteractorImpl.kt
index 8dbe562..52b4bcc 100644
--- a/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/impl/RetailModeInteractorImpl.kt
+++ b/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/impl/RetailModeInteractorImpl.kt
@@ -22,11 +22,9 @@
 import javax.inject.Inject
 
 @SysUISingleton
-class RetailModeInteractorImpl
+public class RetailModeInteractorImpl
 @Inject
-constructor(
-    private val repository: RetailModeRepository,
-) : RetailModeInteractor {
+constructor(private val repository: RetailModeRepository) : RetailModeInteractor {
     override val isInRetailMode: Boolean
         get() = repository.inRetailMode
 }
diff --git a/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxy.kt
index 597276a..a8d4f79 100644
--- a/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxy.kt
+++ b/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxy.kt
@@ -19,6 +19,7 @@
 import android.content.ContentResolver
 import android.database.ContentObserver
 import android.net.Uri
+import android.provider.Settings
 import android.provider.Settings.SettingNotFoundException
 import androidx.annotation.AnyThread
 import androidx.annotation.WorkerThread
@@ -28,6 +29,7 @@
 import kotlin.coroutines.coroutineContext
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
 
 /**
  * Used to interact with mainly with Settings.Global, but can also be used for Settings.System and
@@ -43,15 +45,15 @@
  * This class also provides [.registerContentObserver] methods, normally found on [ContentResolver]
  * instances, unifying setting related actions in one place.
  */
-interface SettingsProxy {
+public interface SettingsProxy {
     /** Returns the [ContentResolver] this instance was constructed with. */
-    fun getContentResolver(): ContentResolver
+    public fun getContentResolver(): ContentResolver
 
     /** Returns the [CoroutineScope] that the async APIs will use. */
-    val settingsScope: CoroutineScope
+    public val settingsScope: CoroutineScope
 
     @OptIn(ExperimentalStdlibApi::class)
-    suspend fun executeOnSettingsScopeDispatcher(name: String, block: () -> Unit) {
+    public suspend fun executeOnSettingsScopeDispatcher(name: String, block: () -> Unit) {
         val settingsDispatcher = settingsScope.coroutineContext[CoroutineDispatcher]
         if (
             settingsDispatcher != null &&
@@ -70,7 +72,7 @@
      * @param name to look up in the table
      * @return the corresponding content URI, or null if not present
      */
-    @AnyThread fun getUriFor(name: String): Uri
+    @AnyThread public fun getUriFor(name: String): Uri
 
     /**
      * Registers listener for a given content observer <b>while blocking the current thread</b>.
@@ -80,7 +82,7 @@
      * [registerContentObserverAsync] instead.
      */
     @WorkerThread
-    fun registerContentObserverSync(name: String, settingsObserver: ContentObserver) {
+    public fun registerContentObserverSync(name: String, settingsObserver: ContentObserver) {
         registerContentObserverSync(getUriFor(name), settingsObserver)
     }
 
@@ -91,7 +93,7 @@
      * registration happens on a worker thread. Caller may wrap the API in an async block if they
      * wish to synchronize execution.
      */
-    suspend fun registerContentObserver(name: String, settingsObserver: ContentObserver) {
+    public suspend fun registerContentObserver(name: String, settingsObserver: ContentObserver) {
         executeOnSettingsScopeDispatcher("registerContentObserver-A") {
             registerContentObserverSync(getUriFor(name), settingsObserver)
         }
@@ -103,7 +105,7 @@
      * API corresponding to [registerContentObserver] for Java usage.
      */
     @AnyThread
-    fun registerContentObserverAsync(name: String, settingsObserver: ContentObserver) =
+    public fun registerContentObserverAsync(name: String, settingsObserver: ContentObserver): Job =
         settingsScope.launch("registerContentObserverAsync-A") {
             registerContentObserverSync(getUriFor(name), settingsObserver)
         }
@@ -116,11 +118,11 @@
      * value.
      */
     @AnyThread
-    fun registerContentObserverAsync(
+    public fun registerContentObserverAsync(
         name: String,
         settingsObserver: ContentObserver,
         @WorkerThread registered: Runnable,
-    ) =
+    ): Job =
         settingsScope.launch("registerContentObserverAsync-B") {
             registerContentObserverSync(getUriFor(name), settingsObserver)
             registered.run()
@@ -133,8 +135,9 @@
      * [registerContentObserverAsync] instead.
      */
     @WorkerThread
-    fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) =
+    public fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) {
         registerContentObserverSync(uri, false, settingsObserver)
+    }
 
     /**
      * Convenience wrapper around [ContentResolver.registerContentObserver].'
@@ -143,7 +146,7 @@
      * registration happens on a worker thread. Caller may wrap the API in an async block if they
      * wish to synchronize execution.
      */
-    suspend fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) {
+    public suspend fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) {
         executeOnSettingsScopeDispatcher("registerContentObserver-B") {
             registerContentObserverSync(uri, settingsObserver)
         }
@@ -155,7 +158,7 @@
      * API corresponding to [registerContentObserver] for Java usage.
      */
     @AnyThread
-    fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) =
+    public fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver): Job =
         settingsScope.launch("registerContentObserverAsync-C") {
             registerContentObserverSync(uri, settingsObserver)
         }
@@ -168,11 +171,11 @@
      * value.
      */
     @AnyThread
-    fun registerContentObserverAsync(
+    public fun registerContentObserverAsync(
         uri: Uri,
         settingsObserver: ContentObserver,
         @WorkerThread registered: Runnable,
-    ) =
+    ): Job =
         settingsScope.launch("registerContentObserverAsync-D") {
             registerContentObserverSync(uri, settingsObserver)
             registered.run()
@@ -184,11 +187,13 @@
      * Implicitly calls [getUriFor] on the passed in name.
      */
     @WorkerThread
-    fun registerContentObserverSync(
+    public fun registerContentObserverSync(
         name: String,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
-    ) = registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
+    ) {
+        registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
+    }
 
     /**
      * Convenience wrapper around [ContentResolver.registerContentObserver].'
@@ -197,7 +202,7 @@
      * registration happens on a worker thread. Caller may wrap the API in an async block if they
      * wish to synchronize execution.
      */
-    suspend fun registerContentObserver(
+    public suspend fun registerContentObserver(
         name: String,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
@@ -213,11 +218,11 @@
      * API corresponding to [registerContentObserver] for Java usage.
      */
     @AnyThread
-    fun registerContentObserverAsync(
+    public fun registerContentObserverAsync(
         name: String,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
-    ) =
+    ): Job =
         settingsScope.launch("registerContentObserverAsync-E") {
             registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
         }
@@ -230,12 +235,12 @@
      * value.
      */
     @AnyThread
-    fun registerContentObserverAsync(
+    public fun registerContentObserverAsync(
         name: String,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
         @WorkerThread registered: Runnable,
-    ) =
+    ): Job =
         settingsScope.launch("registerContentObserverAsync-F") {
             registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
             registered.run()
@@ -248,7 +253,7 @@
      * [registerContentObserverAsync] instead.
      */
     @WorkerThread
-    fun registerContentObserverSync(
+    public fun registerContentObserverSync(
         uri: Uri,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
@@ -266,7 +271,7 @@
      * registration happens on a worker thread. Caller may wrap the API in an async block if they
      * wish to synchronize execution.
      */
-    suspend fun registerContentObserver(
+    public suspend fun registerContentObserver(
         uri: Uri,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
@@ -282,11 +287,11 @@
      * API corresponding to [registerContentObserver] for Java usage.
      */
     @AnyThread
-    fun registerContentObserverAsync(
+    public fun registerContentObserverAsync(
         uri: Uri,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
-    ) =
+    ): Job =
         settingsScope.launch("registerContentObserverAsync-G") {
             registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
         }
@@ -299,12 +304,12 @@
      * value.
      */
     @AnyThread
-    fun registerContentObserverAsync(
+    public fun registerContentObserverAsync(
         uri: Uri,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
         @WorkerThread registered: Runnable,
-    ) =
+    ): Job =
         settingsScope.launch("registerContentObserverAsync-H") {
             registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
             registered.run()
@@ -317,7 +322,7 @@
      * [unregisterContentObserverAsync] instead.
      */
     @WorkerThread
-    fun unregisterContentObserverSync(settingsObserver: ContentObserver) {
+    public fun unregisterContentObserverSync(settingsObserver: ContentObserver) {
         trace({ "SP#unregisterObserver" }) {
             getContentResolver().unregisterContentObserver(settingsObserver)
         }
@@ -330,7 +335,7 @@
      * [ContentObserver] un-registration happens on a worker thread. Caller may wrap the API in an
      * async block if they wish to synchronize execution.
      */
-    suspend fun unregisterContentObserver(settingsObserver: ContentObserver) {
+    public suspend fun unregisterContentObserver(settingsObserver: ContentObserver) {
         executeOnSettingsScopeDispatcher("unregisterContentObserver") {
             unregisterContentObserverSync(settingsObserver)
         }
@@ -343,7 +348,7 @@
      * [ContentObserver] registration happens on a worker thread.
      */
     @AnyThread
-    fun unregisterContentObserverAsync(settingsObserver: ContentObserver) =
+    public fun unregisterContentObserverAsync(settingsObserver: ContentObserver): Job =
         settingsScope.launch("unregisterContentObserverAsync") {
             unregisterContentObserver(settingsObserver)
         }
@@ -354,7 +359,7 @@
      * @param name to look up in the table
      * @return the corresponding value, or null if not present
      */
-    fun getString(name: String): String?
+    public fun getString(name: String): String?
 
     /**
      * Store a name/value pair into the database.
@@ -363,7 +368,7 @@
      * @param value to associate with the name
      * @return true if the value was set, false on database errors
      */
-    fun putString(name: String, value: String?): Boolean
+    public fun putString(name: String, value: String?): Boolean
 
     /**
      * Store a name/value pair into the database.
@@ -394,7 +399,7 @@
      * @return true if the value was set, false on database errors.
      * @see .resetToDefaults
      */
-    fun putString(name: String, value: String?, tag: String?, makeDefault: Boolean): Boolean
+    public fun putString(name: String, value: String?, tag: String?, makeDefault: Boolean): Boolean
 
     /**
      * Convenience function for retrieving a single secure settings value as an integer. Note that
@@ -406,7 +411,7 @@
      * @param default Value to return if the setting is not defined.
      * @return The setting's current value, or default if it is not defined or not a valid integer.
      */
-    fun getInt(name: String, default: Int): Int {
+    public fun getInt(name: String, default: Int): Int {
         val v = getString(name)
         return try {
             v?.toInt() ?: default
@@ -429,7 +434,7 @@
      *   found or the setting value is not an integer.
      */
     @Throws(SettingNotFoundException::class)
-    fun getInt(name: String): Int {
+    public fun getInt(name: String): Int {
         val v = getString(name) ?: throw SettingNotFoundException(name)
         return try {
             v.toInt()
@@ -448,7 +453,7 @@
      * @param value The new value for the setting.
      * @return true if the value was set, false on database errors
      */
-    fun putInt(name: String, value: Int): Boolean {
+    public fun putInt(name: String, value: Int): Boolean {
         return putString(name, value.toString())
     }
 
@@ -462,7 +467,7 @@
      * @param default Value to return if the setting is not defined.
      * @return The setting's current value, or default if it is not defined or not a valid boolean.
      */
-    fun getBool(name: String, default: Boolean): Boolean {
+    public fun getBool(name: String, default: Boolean): Boolean {
         return getInt(name, if (default) 1 else 0) != 0
     }
 
@@ -480,7 +485,7 @@
      *   found or the setting value is not a boolean.
      */
     @Throws(SettingNotFoundException::class)
-    fun getBool(name: String): Boolean {
+    public fun getBool(name: String): Boolean {
         return getInt(name) != 0
     }
 
@@ -494,7 +499,7 @@
      * @param value The new value for the setting.
      * @return true if the value was set, false on database errors
      */
-    fun putBool(name: String, value: Boolean): Boolean {
+    public fun putBool(name: String, value: Boolean): Boolean {
         return putInt(name, if (value) 1 else 0)
     }
 
@@ -508,7 +513,7 @@
      * @param def Value to return if the setting is not defined.
      * @return The setting's current value, or 'def' if it is not defined or not a valid `long`.
      */
-    fun getLong(name: String, def: Long): Long {
+    public fun getLong(name: String, def: Long): Long {
         val valString = getString(name)
         return parseLongOrUseDefault(valString, def)
     }
@@ -527,7 +532,7 @@
      *   found or the setting value is not an integer.
      */
     @Throws(SettingNotFoundException::class)
-    fun getLong(name: String): Long {
+    public fun getLong(name: String): Long {
         val valString = getString(name)
         return parseLongOrThrow(name, valString)
     }
@@ -542,7 +547,7 @@
      * @param value The new value for the setting.
      * @return true if the value was set, false on database errors
      */
-    fun putLong(name: String, value: Long): Boolean {
+    public fun putLong(name: String, value: Long): Boolean {
         return putString(name, value.toString())
     }
 
@@ -556,7 +561,7 @@
      * @param def Value to return if the setting is not defined.
      * @return The setting's current value, or 'def' if it is not defined or not a valid float.
      */
-    fun getFloat(name: String, def: Float): Float {
+    public fun getFloat(name: String, def: Float): Float {
         val v = getString(name)
         return parseFloat(v, def)
     }
@@ -575,7 +580,7 @@
      *   found or the setting value is not a float.
      */
     @Throws(SettingNotFoundException::class)
-    fun getFloat(name: String): Float {
+    public fun getFloat(name: String): Float {
         val v = getString(name)
         return parseFloatOrThrow(name, v)
     }
@@ -590,14 +595,14 @@
      * @param value The new value for the setting.
      * @return true if the value was set, false on database errors
      */
-    fun putFloat(name: String, value: Float): Boolean {
+    public fun putFloat(name: String, value: Float): Boolean {
         return putString(name, value.toString())
     }
 
-    companion object {
+    public companion object {
         /** Convert a string to a long, or uses a default if the string is malformed or null */
         @JvmStatic
-        fun parseLongOrUseDefault(valString: String?, default: Long): Long {
+        public fun parseLongOrUseDefault(valString: String?, default: Long): Long {
             val value: Long =
                 try {
                     valString?.toLong() ?: default
@@ -610,7 +615,7 @@
         /** Convert a string to a long, or throws an exception if the string is malformed or null */
         @JvmStatic
         @Throws(SettingNotFoundException::class)
-        fun parseLongOrThrow(name: String, valString: String?): Long {
+        public fun parseLongOrThrow(name: String, valString: String?): Long {
             if (valString == null) {
                 throw SettingNotFoundException(name)
             }
@@ -623,7 +628,7 @@
 
         /** Convert a string to a float, or uses a default if the string is malformed or null */
         @JvmStatic
-        fun parseFloat(v: String?, def: Float): Float {
+        public fun parseFloat(v: String?, def: Float): Float {
             return try {
                 v?.toFloat() ?: def
             } catch (e: NumberFormatException) {
@@ -636,7 +641,7 @@
          */
         @JvmStatic
         @Throws(SettingNotFoundException::class)
-        fun parseFloatOrThrow(name: String, v: String?): Float {
+        public fun parseFloatOrThrow(name: String, v: String?): Float {
             if (v == null) {
                 throw SettingNotFoundException(name)
             }
@@ -648,7 +653,7 @@
         }
     }
 
-    fun interface CurrentUserIdProvider {
-        @UserIdInt fun getUserId(): Int
+    public fun interface CurrentUserIdProvider {
+        @UserIdInt public fun getUserId(): Int
     }
 }
diff --git a/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxyExt.kt b/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxyExt.kt
index 36468144..60feaf1 100644
--- a/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxyExt.kt
+++ b/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxyExt.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.util.settings
 
+import android.annotation.SuppressLint
 import android.annotation.UserIdInt
 import android.database.ContentObserver
 import com.android.systemui.Flags
@@ -25,10 +26,11 @@
 import kotlinx.coroutines.flow.Flow
 
 /** Kotlin extension functions for [SettingsProxy]. */
-object SettingsProxyExt {
+@SuppressLint("RegisterContentObserverSyncWarning")
+public object SettingsProxyExt {
 
     /** Returns a flow of [Unit] that is invoked each time that content is updated. */
-    fun UserSettingsProxy.observerFlow(
+    public fun UserSettingsProxy.observerFlow(
         @UserIdInt userId: Int,
         vararg names: String,
     ): Flow<Unit> {
@@ -59,9 +61,7 @@
     }
 
     /** Returns a flow of [Unit] that is invoked each time that content is updated. */
-    fun SettingsProxy.observerFlow(
-        vararg names: String,
-    ): Flow<Unit> {
+    public fun SettingsProxy.observerFlow(vararg names: String): Flow<Unit> {
         return conflatedCallbackFlow {
             val observer =
                 object : ContentObserver(null) {
diff --git a/packages/SystemUI/pods/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/pods/com/android/systemui/util/settings/UserSettingsProxy.kt
index 3ccac9e3..61c7f73 100644
--- a/packages/SystemUI/pods/com/android/systemui/util/settings/UserSettingsProxy.kt
+++ b/packages/SystemUI/pods/com/android/systemui/util/settings/UserSettingsProxy.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.util.settings
 
+import android.annotation.SuppressLint
 import android.annotation.UserIdInt
 import android.annotation.WorkerThread
 import android.content.ContentResolver
@@ -28,6 +29,7 @@
 import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloatOrThrow
 import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrThrow
 import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrUseDefault
+import kotlinx.coroutines.Job
 
 /**
  * Used to interact with per-user Settings.Secure and Settings.System settings (but not
@@ -42,11 +44,12 @@
  * This class also provides [.registerContentObserver] methods, normally found on [ContentResolver]
  * instances, unifying setting related actions in one place.
  */
-interface UserSettingsProxy : SettingsProxy {
-    val currentUserProvider: SettingsProxy.CurrentUserIdProvider
+@SuppressLint("RegisterContentObserverSyncWarning")
+public interface UserSettingsProxy : SettingsProxy {
+    public val currentUserProvider: SettingsProxy.CurrentUserIdProvider
 
     /** Returns the user id for the associated [ContentResolver]. */
-    var userId: Int
+    public var userId: Int
         get() = getContentResolver().userId
         set(_) {
             throw UnsupportedOperationException(
@@ -58,7 +61,7 @@
      * Returns the actual current user handle when querying with the current user. Otherwise,
      * returns the passed in user id.
      */
-    fun getRealUserHandle(userHandle: Int): Int {
+    public fun getRealUserHandle(userHandle: Int): Int {
         return if (userHandle != UserHandle.USER_CURRENT) {
             userHandle
         } else currentUserProvider.getUserId()
@@ -75,7 +78,7 @@
         }
     }
 
-    override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) =
+    override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver): Job =
         settingsScope.launch("registerContentObserverAsync-A") {
             registerContentObserverForUserSync(uri, settingsObserver, userId)
         }
@@ -109,7 +112,7 @@
         uri: Uri,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
-    ) =
+    ): Job =
         settingsScope.launch("registerContentObserverAsync-B") {
             registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
         }
@@ -120,7 +123,7 @@
      * Implicitly calls [getUriFor] on the passed in name.
      */
     @WorkerThread
-    fun registerContentObserverForUserSync(
+    public fun registerContentObserverForUserSync(
         name: String,
         settingsObserver: ContentObserver,
         userHandle: Int,
@@ -135,7 +138,7 @@
      * [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
      * async block if they wish to synchronize execution.
      */
-    suspend fun registerContentObserverForUser(
+    public suspend fun registerContentObserverForUser(
         name: String,
         settingsObserver: ContentObserver,
         userHandle: Int,
@@ -150,11 +153,11 @@
      *
      * API corresponding to [registerContentObserverForUser] for Java usage.
      */
-    fun registerContentObserverForUserAsync(
+    public fun registerContentObserverForUserAsync(
         name: String,
         settingsObserver: ContentObserver,
         userHandle: Int,
-    ) =
+    ): Job =
         settingsScope.launch("registerContentObserverForUserAsync-A") {
             try {
                 registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle)
@@ -165,7 +168,7 @@
 
     /** Convenience wrapper around [ContentResolver.registerContentObserver] */
     @WorkerThread
-    fun registerContentObserverForUserSync(
+    public fun registerContentObserverForUserSync(
         uri: Uri,
         settingsObserver: ContentObserver,
         userHandle: Int,
@@ -180,7 +183,7 @@
      * [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
      * async block if they wish to synchronize execution.
      */
-    suspend fun registerContentObserverForUser(
+    public suspend fun registerContentObserverForUser(
         uri: Uri,
         settingsObserver: ContentObserver,
         userHandle: Int,
@@ -195,11 +198,11 @@
      *
      * API corresponding to [registerContentObserverForUser] for Java usage.
      */
-    fun registerContentObserverForUserAsync(
+    public fun registerContentObserverForUserAsync(
         uri: Uri,
         settingsObserver: ContentObserver,
         userHandle: Int,
-    ) =
+    ): Job =
         settingsScope.launch("registerContentObserverForUserAsync-B") {
             try {
                 registerContentObserverForUserSync(uri, settingsObserver, userHandle)
@@ -215,12 +218,12 @@
      * complete, the callback block is called on the <b>background thread</b> to allow for update of
      * value.
      */
-    fun registerContentObserverForUserAsync(
+    public fun registerContentObserverForUserAsync(
         uri: Uri,
         settingsObserver: ContentObserver,
         userHandle: Int,
         @WorkerThread registered: Runnable,
-    ) =
+    ): Job =
         settingsScope.launch("registerContentObserverForUserAsync-C") {
             try {
                 registerContentObserverForUserSync(uri, settingsObserver, userHandle)
@@ -236,7 +239,7 @@
      * Implicitly calls [getUriFor] on the passed in name.
      */
     @WorkerThread
-    fun registerContentObserverForUserSync(
+    public fun registerContentObserverForUserSync(
         name: String,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
@@ -257,7 +260,7 @@
      * [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
      * async block if they wish to synchronize execution.
      */
-    suspend fun registerContentObserverForUser(
+    public suspend fun registerContentObserverForUser(
         name: String,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
@@ -278,7 +281,7 @@
      *
      * API corresponding to [registerContentObserverForUser] for Java usage.
      */
-    fun registerContentObserverForUserAsync(
+    public fun registerContentObserverForUserAsync(
         name: String,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
@@ -300,7 +303,7 @@
 
     /** Convenience wrapper around [ContentResolver.registerContentObserver] */
     @WorkerThread
-    fun registerContentObserverForUserSync(
+    public fun registerContentObserverForUserSync(
         uri: Uri,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
@@ -314,7 +317,6 @@
                     settingsObserver,
                     getRealUserHandle(userHandle),
                 )
-            Unit
         }
     }
 
@@ -325,7 +327,7 @@
      * [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
      * async block if they wish to synchronize execution.
      */
-    suspend fun registerContentObserverForUser(
+    public suspend fun registerContentObserverForUser(
         uri: Uri,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
@@ -346,12 +348,12 @@
      *
      * API corresponding to [registerContentObserverForUser] for Java usage.
      */
-    fun registerContentObserverForUserAsync(
+    public fun registerContentObserverForUserAsync(
         uri: Uri,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
         userHandle: Int,
-    ) =
+    ): Job =
         settingsScope.launch("registerContentObserverForUserAsync-E") {
             try {
                 registerContentObserverForUserSync(
@@ -376,7 +378,7 @@
     }
 
     /** See [getString]. */
-    fun getStringForUser(name: String, userHandle: Int): String?
+    public fun getStringForUser(name: String, userHandle: Int): String?
 
     /**
      * Store a name/value pair into the database. Values written by this method will be overridden
@@ -386,17 +388,17 @@
      * @param value to associate with the name
      * @return true if the value was set, false on database errors
      */
-    fun putString(name: String, value: String?, overrideableByRestore: Boolean): Boolean
+    public fun putString(name: String, value: String?, overrideableByRestore: Boolean): Boolean
 
     override fun putString(name: String, value: String?): Boolean {
         return putStringForUser(name, value, userId)
     }
 
     /** Similar implementation to [putString] for the specified [userHandle]. */
-    fun putStringForUser(name: String, value: String?, userHandle: Int): Boolean
+    public fun putStringForUser(name: String, value: String?, userHandle: Int): Boolean
 
     /** Similar implementation to [putString] for the specified [userHandle]. */
-    fun putStringForUser(
+    public fun putStringForUser(
         name: String,
         value: String?,
         tag: String?,
@@ -410,7 +412,7 @@
     }
 
     /** Similar implementation to [getInt] for the specified [userHandle]. */
-    fun getIntForUser(name: String, default: Int, userHandle: Int): Int {
+    public fun getIntForUser(name: String, default: Int, userHandle: Int): Int {
         val v = getStringForUser(name, userHandle)
         return try {
             v?.toInt() ?: default
@@ -420,11 +422,11 @@
     }
 
     @Throws(SettingNotFoundException::class)
-    override fun getInt(name: String) = getIntForUser(name, userId)
+    override fun getInt(name: String): Int = getIntForUser(name, userId)
 
     /** Similar implementation to [getInt] for the specified [userHandle]. */
     @Throws(SettingNotFoundException::class)
-    fun getIntForUser(name: String, userHandle: Int): Int {
+    public fun getIntForUser(name: String, userHandle: Int): Int {
         val v = getStringForUser(name, userHandle) ?: throw SettingNotFoundException(name)
         return try {
             v.toInt()
@@ -433,24 +435,24 @@
         }
     }
 
-    override fun putInt(name: String, value: Int) = putIntForUser(name, value, userId)
+    override fun putInt(name: String, value: Int): Boolean = putIntForUser(name, value, userId)
 
     /** Similar implementation to [getInt] for the specified [userHandle]. */
-    fun putIntForUser(name: String, value: Int, userHandle: Int) =
+    public fun putIntForUser(name: String, value: Int, userHandle: Int): Boolean =
         putStringForUser(name, value.toString(), userHandle)
 
-    override fun getBool(name: String, def: Boolean) = getBoolForUser(name, def, userId)
+    override fun getBool(name: String, def: Boolean): Boolean = getBoolForUser(name, def, userId)
 
     /** Similar implementation to [getBool] for the specified [userHandle]. */
-    fun getBoolForUser(name: String, def: Boolean, userHandle: Int) =
+    public fun getBoolForUser(name: String, def: Boolean, userHandle: Int): Boolean =
         getIntForUser(name, if (def) 1 else 0, userHandle) != 0
 
     @Throws(SettingNotFoundException::class)
-    override fun getBool(name: String) = getBoolForUser(name, userId)
+    override fun getBool(name: String): Boolean = getBoolForUser(name, userId)
 
     /** Similar implementation to [getBool] for the specified [userHandle]. */
     @Throws(SettingNotFoundException::class)
-    fun getBoolForUser(name: String, userHandle: Int): Boolean {
+    public fun getBoolForUser(name: String, userHandle: Int): Boolean {
         return getIntForUser(name, userHandle) != 0
     }
 
@@ -459,40 +461,40 @@
     }
 
     /** Similar implementation to [putBool] for the specified [userHandle]. */
-    fun putBoolForUser(name: String, value: Boolean, userHandle: Int) =
+    public fun putBoolForUser(name: String, value: Boolean, userHandle: Int): Boolean =
         putIntForUser(name, if (value) 1 else 0, userHandle)
 
     /** Similar implementation to [getLong] for the specified [userHandle]. */
-    fun getLongForUser(name: String, def: Long, userHandle: Int): Long {
+    public fun getLongForUser(name: String, def: Long, userHandle: Int): Long {
         val valString = getStringForUser(name, userHandle)
         return parseLongOrUseDefault(valString, def)
     }
 
     /** Similar implementation to [getLong] for the specified [userHandle]. */
     @Throws(SettingNotFoundException::class)
-    fun getLongForUser(name: String, userHandle: Int): Long {
+    public fun getLongForUser(name: String, userHandle: Int): Long {
         val valString = getStringForUser(name, userHandle)
         return parseLongOrThrow(name, valString)
     }
 
     /** Similar implementation to [putLong] for the specified [userHandle]. */
-    fun putLongForUser(name: String, value: Long, userHandle: Int) =
+    public fun putLongForUser(name: String, value: Long, userHandle: Int): Boolean =
         putStringForUser(name, value.toString(), userHandle)
 
     /** Similar implementation to [getFloat] for the specified [userHandle]. */
-    fun getFloatForUser(name: String, def: Float, userHandle: Int): Float {
+    public fun getFloatForUser(name: String, def: Float, userHandle: Int): Float {
         val v = getStringForUser(name, userHandle)
         return parseFloat(v, def)
     }
 
     /** Similar implementation to [getFloat] for the specified [userHandle]. */
     @Throws(SettingNotFoundException::class)
-    fun getFloatForUser(name: String, userHandle: Int): Float {
+    public fun getFloatForUser(name: String, userHandle: Int): Float {
         val v = getStringForUser(name, userHandle)
         return parseFloatOrThrow(name, v)
     }
 
     /** Similar implementation to [putFloat] for the specified [userHandle]. */
-    fun putFloatForUser(name: String, value: Float, userHandle: Int) =
+    public fun putFloatForUser(name: String, value: Float, userHandle: Int): Boolean =
         putStringForUser(name, value.toString(), userHandle)
 }
diff --git a/packages/SystemUI/res/layout/volume_dialog_top_section.xml b/packages/SystemUI/res/layout/volume_dialog_top_section.xml
index 4fc20e2..29f5248 100644
--- a/packages/SystemUI/res/layout/volume_dialog_top_section.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_top_section.xml
@@ -22,7 +22,6 @@
     android:clipChildren="false"
     android:clipToPadding="false"
     android:gravity="center"
-    android:layoutDirection="ltr"
     android:paddingEnd="@dimen/volume_dialog_ringer_drawer_diff_end_margin"
     app:layoutDescription="@xml/volume_dialog_ringer_drawer_motion_scene">
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 63101d4..bd09e39 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -199,7 +199,7 @@
  * to be updated.
  */
 @SysUISingleton
-public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpable, CoreStartable {
+public class KeyguardUpdateMonitor implements TrustManager.TrustListener, CoreStartable {
 
     private static final String TAG = "KeyguardUpdateMonitor";
     private static final int BIOMETRIC_LOCKOUT_RESET_DELAY_MS = 600;
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index e725353..19da5de 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -112,7 +112,7 @@
  */
 @SysUISingleton
 public class ScreenDecorations implements
-        CoreStartable, ConfigurationController.ConfigurationListener, Dumpable {
+        CoreStartable, ConfigurationController.ConfigurationListener {
     private static final boolean DEBUG_LOGGING = false;
     private static final String TAG = "ScreenDecorations";
 
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 85f1880..c78f75a 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -54,6 +54,7 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
 import com.android.wm.shell.animation.FlingAnimationUtils;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
@@ -890,12 +891,16 @@
         if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_DRAG_TO_CONTENTS)) {
             if (v instanceof ExpandableNotificationRow) {
                 ExpandableNotificationRow enr = (ExpandableNotificationRow) v;
-                boolean canBubble = enr.getEntry().canBubble();
-                Notification notif = enr.getEntry().getSbn().getNotification();
-                PendingIntent dragIntent = notif.contentIntent != null ? notif.contentIntent
-                        : notif.fullScreenIntent;
-                if (dragIntent != null && dragIntent.isActivity() && !canBubble) {
-                    return true;
+                if (NotificationBundleUi.isEnabled()) {
+                    return enr.getEntryAdapter().canDragAndDrop();
+                } else {
+                    boolean canBubble = enr.getEntry().canBubble();
+                    Notification notif = enr.getEntry().getSbn().getNotification();
+                    PendingIntent dragIntent = notif.contentIntent != null ? notif.contentIntent
+                            : notif.fullScreenIntent;
+                    if (dragIntent != null && dragIntent.isActivity() && !canBubble) {
+                        return true;
+                    }
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 786d27a..b730c93 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -289,6 +289,8 @@
             List<DeviceItem> hearingDeviceItemList = getHearingDeviceItemList();
             CachedBluetoothDevice activeHearingDevice = getActiveHearingDevice(
                     hearingDeviceItemList);
+            mLocalBluetoothManager.getEventManager().registerCallback(this);
+
             mMainExecutor.execute(() -> {
                 setupDeviceListView(dialog, hearingDeviceItemList);
                 setupPairNewDeviceButton(dialog);
@@ -302,21 +304,6 @@
     }
 
     @Override
-    public void onStart(@NonNull SystemUIDialog dialog) {
-        mBgExecutor.execute(() -> {
-            if (mLocalBluetoothManager != null) {
-                mLocalBluetoothManager.getEventManager().registerCallback(this);
-            }
-            if (mPresetController != null) {
-                mPresetController.registerHapCallback();
-            }
-            if (mAmbientController != null) {
-                mAmbientController.start();
-            }
-        });
-    }
-
-    @Override
     public void onStop(@NonNull SystemUIDialog dialog) {
         mBgExecutor.execute(() -> {
             if (mLocalBluetoothManager != null) {
@@ -378,6 +365,7 @@
 
         mPresetLayout = dialog.requireViewById(R.id.preset_layout);
         mPresetLayout.setVisibility(mPresetController.isPresetControlAvailable() ? VISIBLE : GONE);
+        mBgExecutor.execute(() -> mPresetController.registerHapCallback());
     }
 
     private void setupAmbientControls(CachedBluetoothDevice activeHearingDevice) {
@@ -387,6 +375,7 @@
                 mDialog.getContext(), mLocalBluetoothManager, ambientLayout);
         mAmbientController.setShowUiWhenLocalDataExist(false);
         mAmbientController.loadDevice(activeHearingDevice);
+        mBgExecutor.execute(() -> mAmbientController.start());
     }
 
     private void setupPairNewDeviceButton(SystemUIDialog dialog) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 4dcf268..0902d19 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -34,6 +34,7 @@
 import android.util.RotationUtils
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
+import android.view.accessibility.AccessibilityManager
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.keyguard.AuthInteractionProperties
 import com.android.launcher3.icons.IconProvider
@@ -85,7 +86,17 @@
     private val udfpsUtils: UdfpsUtils,
     private val iconProvider: IconProvider,
     private val activityTaskManager: ActivityTaskManager,
+    private val accessibilityManager: AccessibilityManager,
 ) {
+    // When a11y enabled, increase message delay to ensure messages get read
+    private val messageDelay =
+        accessibilityManager
+            .getRecommendedTimeoutMillis(
+                BiometricPrompt.HIDE_DIALOG_DELAY,
+                AccessibilityManager.FLAG_CONTENT_CONTROLS or AccessibilityManager.FLAG_CONTENT_TEXT,
+            )
+            .toLong()
+
     /** The set of modalities available for this prompt */
     val modalities: Flow<BiometricModalities> =
         promptSelectorInteractor.prompt
@@ -692,7 +703,7 @@
 
         messageJob?.cancel()
         messageJob = launch {
-            delay(BiometricPrompt.HIDE_DIALOG_DELAY.toLong())
+            delay(messageDelay)
             if (authenticateAfterError) {
                 showAuthenticating(messageAfterError)
             } else {
@@ -754,7 +765,7 @@
 
         messageJob?.cancel()
         messageJob = launch {
-            delay(BiometricPrompt.HIDE_DIALOG_DELAY.toLong())
+            delay(messageDelay)
             showAuthenticating(messageAfterHelp)
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index d464200..721d116 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -26,14 +26,16 @@
 import android.os.Handler
 import android.util.Log
 import android.view.Display
+import android.view.IWindowManager
 import com.android.app.tracing.FlowTracing.traceEach
 import com.android.app.tracing.traceSection
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.display.data.DisplayEvent
+import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.util.Compile
 import com.android.systemui.util.kotlin.pairwiseBy
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -43,6 +45,7 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asFlow
+import kotlinx.coroutines.flow.callbackFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
@@ -50,12 +53,13 @@
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.scan
 import kotlinx.coroutines.flow.stateIn
 
-/** Provides a [Flow] of [Display] as returned by [DisplayManager]. */
+/** Repository for providing access to display related information and events. */
 interface DisplayRepository {
     /** Display change event indicating a change to the given displayId has occurred. */
     val displayChangeEvent: Flow<Int>
@@ -66,6 +70,9 @@
     /** Display removal event indicating a display has been removed. */
     val displayRemovalEvent: Flow<Int>
 
+    /** A [StateFlow] that maintains a set of display IDs that should have system decorations. */
+    val displayIdsWithSystemDecorations: StateFlow<Set<Int>>
+
     /**
      * Provides the current set of displays.
      *
@@ -124,6 +131,8 @@
 @Inject
 constructor(
     private val displayManager: DisplayManager,
+    private val commandQueue: CommandQueue,
+    private val windowManager: IWindowManager,
     @Background backgroundHandler: Handler,
     @Background bgApplicationScope: CoroutineScope,
     @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
@@ -426,6 +435,56 @@
             .map { it.resultSet }
     }
 
+    private val decorationEvents: Flow<Event> = callbackFlow {
+        val callback =
+            object : CommandQueue.Callbacks {
+                override fun onDisplayAddSystemDecorations(displayId: Int) {
+                    trySend(Event.Add(displayId))
+                }
+
+                override fun onDisplayRemoveSystemDecorations(displayId: Int) {
+                    trySend(Event.Remove(displayId))
+                }
+            }
+        commandQueue.addCallback(callback)
+        awaitClose { commandQueue.removeCallback(callback) }
+    }
+
+    private val initialDisplayIdsWithDecorations: Set<Int> =
+        displayIds.value.filter { windowManager.shouldShowSystemDecors(it) }.toSet()
+
+    /**
+     * A [StateFlow] that maintains a set of display IDs that should have system decorations.
+     *
+     * Updates to the set are triggered by:
+     * - Adding displays via [CommandQueue.Callbacks.onDisplayAddSystemDecorations].
+     * - Removing displays via [CommandQueue.Callbacks.onDisplayRemoveSystemDecorations].
+     * - Removing displays via [displayRemovalEvent] emissions.
+     *
+     * The set is initialized with displays that qualify for system decorations based on
+     * [WindowManager.shouldShowSystemDecors].
+     */
+    override val displayIdsWithSystemDecorations: StateFlow<Set<Int>> =
+        merge(decorationEvents, displayRemovalEvent.map { Event.Remove(it) })
+            .scan(initialDisplayIdsWithDecorations) { displayIds: Set<Int>, event: Event ->
+                when (event) {
+                    is Event.Add -> displayIds + event.displayId
+                    is Event.Remove -> displayIds - event.displayId
+                }
+            }
+            .distinctUntilChanged()
+            .stateIn(
+                scope = bgApplicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = initialDisplayIdsWithDecorations,
+            )
+
+    private sealed class Event(val displayId: Int) {
+        class Add(displayId: Int) : Event(displayId)
+
+        class Remove(displayId: Int) : Event(displayId)
+    }
+
     private companion object {
         const val TAG = "DisplayRepository"
         val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index efa9c21..cc0efbc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -241,7 +241,7 @@
  * directly to the keyguard UI is posted to a {@link android.os.Handler} to ensure it is taken on the UI
  * thread of the keyguard.
  */
-public class KeyguardViewMediator implements CoreStartable, Dumpable,
+public class KeyguardViewMediator implements CoreStartable,
         StatusBarStateController.StateListener {
 
     private static final boolean ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index 7a4be1d..fc5914b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -22,6 +22,7 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.Flags
 import com.android.systemui.customization.R as customR
 import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
@@ -81,7 +82,7 @@
 
                             logger.logConstraintSet(cs, clockViewModel)
                             cs.applyTo(constraintLayout)
-                            if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+                            if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                                 manuallySetDateWeatherConstraintsOnConstraintLayout(
                                     cs,
                                     constraintLayout,
@@ -110,7 +111,7 @@
                                 }
                             logger.logConstraintSet(cs, clockViewModel)
                             cs.applyTo(constraintLayout)
-                            if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+                            if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                                 manuallySetDateWeatherConstraintsOnConstraintLayout(
                                     cs,
                                     constraintLayout,
@@ -168,7 +169,7 @@
             str1 = "${cs.getConstraint(smartspaceDateId).propertySet.alpha}"
         }
 
-        if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+        if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
             this.i({ "applyCsToSmartspaceWeather: vis=${getVisText(int1)}; alpha=$str1" }) {
                 val smartspaceDateId = sharedR.id.weather_smartspace_view
                 int1 = cs.getVisibility(smartspaceDateId)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index b69df68..45801ba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -89,7 +89,6 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.flow.update
 
@@ -189,7 +188,7 @@
                         viewModel.translationY.collect { y ->
                             childViews[burnInLayerId]?.translationY = y
                             childViews[largeClockId]?.translationY = y
-                            if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+                            if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                                 childViews[largeClockDateId]?.translationY = y
                                 childViews[largeClockWeatherId]?.translationY = y
                             }
@@ -381,17 +380,9 @@
                     if (wallpaperFocalAreaViewModel.hasFocalArea.value) {
                         launch {
                             wallpaperFocalAreaViewModel.wallpaperFocalAreaBounds.collect {
-                                wallpaperFocalAreaBounds ->
-                                wallpaperFocalAreaViewModel.setFocalAreaBounds(
-                                    wallpaperFocalAreaBounds
-                                )
+                                wallpaperFocalAreaViewModel.setFocalAreaBounds(it)
                             }
                         }
-                        launch {
-                            wallpaperFocalAreaViewModel.wallpaperFocalAreaBounds
-                                .filterNotNull()
-                                .collect { wallpaperFocalAreaViewModel.setFocalAreaBounds(it) }
-                        }
                     }
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
index 76ece7d..def1ac8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
@@ -80,7 +80,7 @@
                     }
                 }
 
-                if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+                if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                     launch("$TAG#smartspaceViewModel.burnInLayerVisibility") {
                         keyguardRootViewModel.burnInLayerVisibility.collect { visibility ->
                             if (clockViewModel.isLargeClockVisible.value) {
@@ -147,7 +147,7 @@
                 val dateView =
                     constraintLayout.requireViewById<View>(sharedR.id.date_smartspace_view)
                 addView(dateView)
-                if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+                if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                     val weatherView =
                         constraintLayout.requireViewById<View>(sharedR.id.weather_smartspace_view)
                     addView(weatherView)
@@ -169,7 +169,7 @@
                 val dateView =
                     constraintLayout.requireViewById<View>(sharedR.id.date_smartspace_view)
                 removeView(dateView)
-                if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+                if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                     val weatherView =
                         constraintLayout.requireViewById<View>(sharedR.id.weather_smartspace_view)
                     removeView(weatherView)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index 3d5dbb6d..8a33c64 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -121,19 +121,19 @@
             setAlpha(getNonTargetClockFace(clock).views, 0F)
 
             if (!keyguardClockViewModel.isLargeClockVisible.value) {
-                if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+                if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                     connect(
                         sharedR.id.bc_smartspace_view,
                         TOP,
                         customR.id.lockscreen_clock_view,
-                        BOTTOM
+                        BOTTOM,
                     )
                 } else {
                     connect(
                         sharedR.id.bc_smartspace_view,
                         TOP,
                         sharedR.id.date_smartspace_view,
-                        BOTTOM
+                        BOTTOM,
                     )
                 }
             } else {
@@ -161,7 +161,7 @@
             )
             if (
                 rootViewModel.isNotifIconContainerVisible.value.value &&
-                keyguardClockViewModel.hasAodIcons.value
+                    keyguardClockViewModel.hasAodIcons.value
             ) {
                 createBarrier(
                     R.id.weather_clock_date_and_icons_barrier_bottom,
@@ -197,13 +197,13 @@
                 TOP,
             )
             val largeClockTopMargin =
-                if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+                if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                     keyguardClockViewModel.getLargeClockTopMargin() +
-                            getDimen(ENHANCED_SMARTSPACE_HEIGHT)
+                        getDimen(ENHANCED_SMARTSPACE_HEIGHT)
                 } else {
                     keyguardClockViewModel.getLargeClockTopMargin() +
-                            getDimen(DATE_WEATHER_VIEW_HEIGHT) +
-                            getDimen(ENHANCED_SMARTSPACE_HEIGHT)
+                        getDimen(DATE_WEATHER_VIEW_HEIGHT) +
+                        getDimen(ENHANCED_SMARTSPACE_HEIGHT)
                 }
             connect(
                 customR.id.lockscreen_clock_view_large,
@@ -229,9 +229,9 @@
                 PARENT_ID,
                 START,
                 context.resources.getDimensionPixelSize(customR.dimen.clock_padding_start) +
-                        context.resources.getDimensionPixelSize(
-                            customR.dimen.status_view_margin_horizontal
-                        ),
+                    context.resources.getDimensionPixelSize(
+                        customR.dimen.status_view_margin_horizontal
+                    ),
             )
             val smallClockTopMargin = keyguardClockViewModel.getSmallClockTopMargin()
             create(R.id.small_clock_guideline_top, ConstraintSet.HORIZONTAL_GUIDELINE)
@@ -243,18 +243,18 @@
 
             val smallClockBottom =
                 keyguardClockViewModel.getSmallClockTopMargin() +
-                        context.resources.getDimensionPixelSize(customR.dimen.small_clock_height)
+                    context.resources.getDimensionPixelSize(customR.dimen.small_clock_height)
             val marginBetweenSmartspaceAndNotification =
                 context.resources.getDimensionPixelSize(
                     R.dimen.keyguard_status_view_bottom_margin
                 ) +
-                        if (context.resources.getBoolean(R.bool.config_use_large_screen_shade_header)) {
-                            largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
-                        } else {
-                            0
-                        }
+                    if (context.resources.getBoolean(R.bool.config_use_large_screen_shade_header)) {
+                        largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
+                    } else {
+                        0
+                    }
 
-            if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+            if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                 clockInteractor.setNotificationStackDefaultTop(
                     (smallClockBottom + marginBetweenSmartspaceAndNotification).toFloat()
                 )
@@ -263,8 +263,8 @@
                     getDimen(context, DATE_WEATHER_VIEW_HEIGHT).toFloat()
                 clockInteractor.setNotificationStackDefaultTop(
                     smallClockBottom +
-                            dateWeatherSmartspaceHeight +
-                            marginBetweenSmartspaceAndNotification
+                        dateWeatherSmartspaceHeight +
+                        marginBetweenSmartspaceAndNotification
                 )
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index cbd80b4..37cc852f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -80,7 +80,7 @@
         weatherView = smartspaceController.buildAndConnectWeatherView(constraintLayout, false)
         dateView =
             smartspaceController.buildAndConnectDateView(constraintLayout, false) as? ViewGroup
-        if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+        if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
             weatherViewLargeClock =
                 smartspaceController.buildAndConnectWeatherView(constraintLayout, true)
             dateViewLargeClock =
@@ -88,7 +88,7 @@
         }
         pastVisibility = smartspaceView?.visibility ?: View.GONE
         constraintLayout.addView(smartspaceView)
-        if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+        if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
             dateView?.visibility = View.GONE
             weatherView?.visibility = View.GONE
             dateViewLargeClock?.visibility = View.GONE
@@ -139,7 +139,7 @@
         constraintSet.apply {
             constrainHeight(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
             constrainWidth(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
-            if (!com.android.systemui.shared.Flags.clockReactiveVariants()) {
+            if (!com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                 connect(
                     sharedR.id.date_smartspace_view,
                     ConstraintSet.START,
@@ -167,7 +167,7 @@
                 smartspaceHorizontalPadding,
             )
             if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) {
-                if (!com.android.systemui.shared.Flags.clockReactiveVariants()) {
+                if (!com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                     clear(sharedR.id.date_smartspace_view, ConstraintSet.TOP)
                     connect(
                         sharedR.id.date_smartspace_view,
@@ -178,7 +178,7 @@
                 }
             } else {
                 clear(sharedR.id.date_smartspace_view, ConstraintSet.BOTTOM)
-                if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+                if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                     connect(
                         sharedR.id.bc_smartspace_view,
                         ConstraintSet.TOP,
@@ -201,7 +201,7 @@
                 }
             }
 
-            if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+            if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                 if (keyguardClockViewModel.isLargeClockVisible.value) {
                     setVisibility(sharedR.id.weather_smartspace_view, GONE)
                     setVisibility(sharedR.id.date_smartspace_view, GONE)
@@ -335,7 +335,7 @@
                 }
             }
 
-            if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+            if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                 createBarrier(
                     R.id.smart_space_barrier_bottom,
                     Barrier.BOTTOM,
@@ -370,7 +370,7 @@
         if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
 
         val list =
-            if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+            if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                 listOf(
                     smartspaceView,
                     dateView,
@@ -401,7 +401,7 @@
         val weatherId: Int
         val dateId: Int
         if (
-            com.android.systemui.shared.Flags.clockReactiveVariants() &&
+            com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout() &&
                 keyguardClockViewModel.isLargeClockVisible.value
         ) {
             weatherId = sharedR.id.weather_smartspace_view_large
@@ -420,7 +420,7 @@
             setVisibility(dateId, if (showDateView) VISIBLE else GONE)
             setAlpha(dateId, if (showDateView) 1f else 0f)
 
-            if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+            if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                 if (keyguardClockViewModel.isLargeClockVisible.value) {
                     setVisibility(sharedR.id.weather_smartspace_view, GONE)
                     setVisibility(sharedR.id.date_smartspace_view, GONE)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index 67158e2..434d7ea 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -297,14 +297,14 @@
                         logger.e("No large clock set, falling back")
                         addTarget(customR.id.lockscreen_clock_view_large)
                     }
-                if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+                if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                     addTarget(sharedR.id.date_smartspace_view_large)
                     addTarget(sharedR.id.weather_smartspace_view_large)
                 }
             } else {
                 logger.i("Adding small clock")
                 addTarget(customR.id.lockscreen_clock_view)
-                if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+                if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                     addTarget(sharedR.id.date_smartspace_view)
                     addTarget(sharedR.id.weather_smartspace_view)
                 }
@@ -386,7 +386,7 @@
             duration =
                 if (isLargeClock) STATUS_AREA_MOVE_UP_MILLIS else STATUS_AREA_MOVE_DOWN_MILLIS
             interpolator = Interpolators.EMPHASIZED
-            if (!com.android.systemui.shared.Flags.clockReactiveVariants()) {
+            if (!com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
                 addTarget(sharedR.id.date_smartspace_view)
             }
             addTarget(sharedR.id.bc_smartspace_view)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
index 827c61e..0874b6d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
@@ -25,14 +25,12 @@
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.shared.R as sharedR
 
-class DefaultClockSteppingTransition(
-    private val clock: ClockController,
-) : Transition() {
+class DefaultClockSteppingTransition(private val clock: ClockController) : Transition() {
     init {
         interpolator = Interpolators.LINEAR
         duration = KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION_MS
         addTarget(clock.largeClock.view)
-        if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+        if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
             addTarget(sharedR.id.date_smartspace_view_large)
             addTarget(sharedR.id.weather_smartspace_view_large)
         }
@@ -56,7 +54,7 @@
     override fun createAnimator(
         sceneRoot: ViewGroup,
         startValues: TransitionValues?,
-        endValues: TransitionValues?
+        endValues: TransitionValues?,
     ): Animator? {
         if (startValues == null || endValues == null) {
             return null
@@ -72,7 +70,7 @@
             clock.largeClock.animations.onPositionUpdated(
                 fromLeft,
                 direction,
-                animation.animatedFraction
+                animation.animatedFraction,
             )
         }
         return anim
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt
index beb4d41..df0e1ad 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt
@@ -22,6 +22,7 @@
 import android.graphics.drawable.Icon
 import android.media.session.MediaController
 import android.media.session.PlaybackState
+import android.os.BadParcelableException
 import android.util.Log
 import com.android.systemui.Flags.mediaControlsPostsOptimization
 import com.android.systemui.biometrics.Utils.toBitmap
@@ -109,7 +110,12 @@
     }
     if (firstAction.extras != null) {
         firstAction.extras.keySet().forEach { key ->
-            if (firstAction.extras[key] != secondAction.extras[key]) {
+            try {
+                if (firstAction.extras[key] != secondAction.extras[key]) {
+                    return false
+                }
+            } catch (e: BadParcelableException) {
+                Log.e(TAG, "Cannot unparcel extras", e)
                 return false
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
index 34f7c4d..cedf661 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
@@ -127,10 +127,9 @@
         }
 
         holder.seekBar.setMax(data.duration)
-        val totalTimeString =
-            DateUtils.formatElapsedTime(data.duration / DateUtils.SECOND_IN_MILLIS)
+        val totalTimeDescription = data.durationDescription
         if (data.scrubbing) {
-            holder.scrubbingTotalTimeView.text = totalTimeString
+            holder.scrubbingTotalTimeView.text = formatTimeLabel(data.duration)
         }
 
         data.elapsedTime?.let {
@@ -148,20 +147,25 @@
                 }
             }
 
-            val elapsedTimeString = DateUtils.formatElapsedTime(it / DateUtils.SECOND_IN_MILLIS)
+            val elapsedTimeDescription = data.elapsedTimeDescription
             if (data.scrubbing) {
-                holder.scrubbingElapsedTimeView.text = elapsedTimeString
+                holder.scrubbingElapsedTimeView.text = formatTimeLabel(it)
             }
 
             holder.seekBar.contentDescription =
                 holder.seekBar.context.getString(
                     R.string.controls_media_seekbar_description,
-                    elapsedTimeString,
-                    totalTimeString
+                    elapsedTimeDescription,
+                    totalTimeDescription,
                 )
         }
     }
 
+    /** Returns a time string suitable for display, e.g. "12:34" */
+    private fun formatTimeLabel(milliseconds: Int): CharSequence {
+        return DateUtils.formatElapsedTime(milliseconds / DateUtils.SECOND_IN_MILLIS)
+    }
+
     @VisibleForTesting
     open fun buildResetAnimator(targetTime: Int): Animator {
         val animator =
@@ -169,7 +173,7 @@
                 holder.seekBar,
                 "progress",
                 holder.seekBar.progress,
-                targetTime + RESET_ANIMATION_DURATION_MS
+                targetTime + RESET_ANIMATION_DURATION_MS,
             )
         animator.setAutoCancel(true)
         animator.duration = RESET_ANIMATION_DURATION_MS.toLong()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
index 1e99697..a1f0cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
@@ -16,11 +16,15 @@
 
 package com.android.systemui.media.controls.ui.viewmodel
 
+import android.icu.text.MeasureFormat
+import android.icu.util.Measure
+import android.icu.util.MeasureUnit
 import android.media.MediaMetadata
 import android.media.session.MediaController
 import android.media.session.PlaybackState
 import android.os.SystemClock
 import android.os.Trace
+import android.text.format.DateUtils
 import android.view.GestureDetector
 import android.view.MotionEvent
 import android.view.View
@@ -38,11 +42,14 @@
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.NotificationMediaManager
 import com.android.systemui.util.concurrency.RepeatableExecutor
+import java.util.Locale
 import javax.inject.Inject
 import kotlin.math.abs
 
-private const val POSITION_UPDATE_INTERVAL_MILLIS = 100L
+private const val POSITION_UPDATE_INTERVAL_MILLIS = 500L
 private const val MIN_FLING_VELOCITY_SCALE_FACTOR = 10
+private const val MIN_IN_SEC = 60
+private const val HOUR_IN_SEC = MIN_IN_SEC * 60
 
 private const val TRACE_POSITION_NAME = "SeekBarPollingPosition"
 
@@ -97,11 +104,20 @@
         )
         set(value) {
             val enabledChanged = value.enabled != field.enabled
-            field = value
             if (enabledChanged) {
                 enabledChangeListener?.onEnabledChanged(value.enabled)
             }
-            _progress.postValue(value)
+            bgExecutor.execute {
+                val durationDescription = formatTimeContentDescription(value.duration)
+                val elapsedDescription =
+                    value.elapsedTime?.let { formatTimeContentDescription(it) } ?: ""
+                field =
+                    value.copy(
+                        durationDescription = durationDescription,
+                        elapsedTimeDescription = elapsedDescription,
+                    )
+                _progress.postValue(field)
+            }
         }
 
     private val _progress = MutableLiveData<Progress>().apply { postValue(_data) }
@@ -253,7 +269,8 @@
                 playbackState?.state ?: PlaybackState.STATE_NONE
             )
         _data = Progress(enabled, seekAvailable, playing, scrubbing, position, duration, listening)
-        checkIfPollingNeeded()
+        // No need to update since we just set the progress info
+        checkIfPollingNeeded(requireUpdate = false)
     }
 
     /**
@@ -311,8 +328,13 @@
         }
     }
 
+    /**
+     * Begin polling if needed given the current seekbar state
+     *
+     * @param requireUpdate If true, update the playback position without beginning polling
+     */
     @WorkerThread
-    private fun checkIfPollingNeeded() {
+    private fun checkIfPollingNeeded(requireUpdate: Boolean = true) {
         val needed = listening && !scrubbing && playbackState?.isInMotion() ?: false
         val traceCookie = controller?.sessionToken.hashCode()
         if (needed) {
@@ -329,7 +351,7 @@
                     Trace.endAsyncSection(TRACE_POSITION_NAME, traceCookie)
                 }
             }
-        } else {
+        } else if (requireUpdate) {
             checkPlaybackPosition()
             cancel?.run()
             cancel = null
@@ -399,6 +421,43 @@
             abs(firstMotionEvent!!.y - lastMotionEvent!!.y)
     }
 
+    /**
+     * Returns a time string suitable for content description, e.g. "12 minutes 34 seconds"
+     *
+     * Follows same logic as Chronometer#formatDuration
+     */
+    private fun formatTimeContentDescription(milliseconds: Int): CharSequence {
+        var seconds = milliseconds / DateUtils.SECOND_IN_MILLIS
+
+        val hours =
+            if (seconds >= HOUR_IN_SEC) {
+                seconds / HOUR_IN_SEC
+            } else {
+                0
+            }
+        seconds -= hours * HOUR_IN_SEC
+
+        val minutes =
+            if (seconds >= MIN_IN_SEC) {
+                seconds / MIN_IN_SEC
+            } else {
+                0
+            }
+        seconds -= minutes * MIN_IN_SEC
+
+        val measures = arrayListOf<Measure>()
+        if (hours > 0) {
+            measures.add(Measure(hours, MeasureUnit.HOUR))
+        }
+        if (minutes > 0) {
+            measures.add(Measure(minutes, MeasureUnit.MINUTE))
+        }
+        measures.add(Measure(seconds, MeasureUnit.SECOND))
+
+        return MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+            .formatMeasures(*measures.toTypedArray())
+    }
+
     /** Listener interface to be notified when the user starts or stops scrubbing. */
     interface ScrubbingChangeListener {
         fun onScrubbingChanged(scrubbing: Boolean)
@@ -580,5 +639,7 @@
         val duration: Int,
         /** whether seekBar is listening to progress updates */
         val listening: Boolean,
+        val elapsedTimeDescription: CharSequence = "",
+        val durationDescription: CharSequence = "",
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index 6ca0471..469bec7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -24,7 +24,6 @@
 import com.android.internal.logging.UiEventLogger
 import com.android.internal.statusbar.IUndoMediaTransferCallback
 import com.android.systemui.CoreStartable
-import com.android.systemui.Dumpable
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
@@ -53,7 +52,7 @@
     private val dumpManager: DumpManager,
     private val logger: MediaTttSenderLogger,
     private val uiEventLogger: MediaTttSenderUiEventLogger,
-) : CoreStartable, Dumpable {
+) : CoreStartable {
 
     // Since the media transfer display is similar to a heads-up notification, use the same timeout.
     private val defaultTimeout = context.resources.getInteger(R.integer.heads_up_notification_decay)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index e0b93fb..44c8dc3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -21,6 +21,7 @@
 import android.graphics.PointF
 import android.graphics.Rect
 import android.os.Bundle
+import android.os.Trace
 import android.util.IndentingPrintWriter
 import android.view.LayoutInflater
 import android.view.MotionEvent
@@ -112,6 +113,7 @@
 import com.android.systemui.plugins.qs.QSContainerController
 import com.android.systemui.qs.composefragment.SceneKeys.QuickQuickSettings
 import com.android.systemui.qs.composefragment.SceneKeys.QuickSettings
+import com.android.systemui.qs.composefragment.SceneKeys.debugName
 import com.android.systemui.qs.composefragment.SceneKeys.toIdleSceneKey
 import com.android.systemui.qs.composefragment.ui.GridAnchor
 import com.android.systemui.qs.composefragment.ui.NotificationScrimClipParams
@@ -285,6 +287,12 @@
      */
     @Composable
     private fun CollapsableQuickSettingsSTL() {
+        val nextCookie = remember {
+            object {
+                var value = 0
+            }
+        }
+        val transitionToCookie = remember { mutableMapOf<TransitionState.Transition, Int>() }
         val sceneState =
             rememberMutableSceneTransitionLayoutState(
                 initialScene = remember { viewModel.expansionState.toIdleSceneKey() },
@@ -298,6 +306,20 @@
                             toEditMode()
                         }
                     },
+                onTransitionStart = { transition ->
+                    val cookie = nextCookie.value++
+                    transitionToCookie[transition] = cookie
+                    Trace.beginAsyncSection(
+                        "CollapsableQuickSettingsSTL ${transition.debugName}",
+                        cookie,
+                    )
+                },
+                onTransitionEnd = { transition ->
+                    Trace.endAsyncSection(
+                        "CollapsableQuickSettingsSTL ${transition.debugName}",
+                        transitionToCookie.remove(transition) ?: -1,
+                    )
+                },
             )
 
         LaunchedEffect(Unit) {
@@ -854,6 +876,9 @@
     val QuickSettings = SceneKey("QuickSettingsScene")
     val EditMode = SceneKey("EditModeScene")
 
+    val TransitionState.Transition.debugName: String
+        get() = "[from=${fromContent.debugName}, to=${toContent.debugName}]"
+
     fun QSFragmentComposeViewModel.QSExpansionState.toIdleSceneKey(): SceneKey {
         return when {
             progress < 0.5f -> QuickQuickSettings
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 934404d..05ef164 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -562,12 +562,15 @@
         var userId: Int = lockScreenUserManager.getCurrentUserId()
         var entry: NotificationEntry? = null
         if (expandView is ExpandableNotificationRow) {
-            entry = expandView.entry
             expandView.setUserExpanded(/* userExpanded= */ true, /* allowChildExpansion= */ true)
             // Indicate that the group expansion is changing at this time -- this way the group
             // and children backgrounds / divider animations will look correct.
             expandView.isGroupExpansionChanging = true
-            userId = entry.sbn.userId
+            if (NotificationBundleUi.isEnabled) {
+                userId = expandView.entryAdapter?.sbn?.userId!!
+            } else {
+                userId = expandView.entry.sbn.userId
+            }
         }
         var fullShadeNeedsBouncer =
             (!lockScreenUserManager.shouldShowLockscreenNotifications() ||
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/layout/LetterboxBackgroundProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/layout/LetterboxBackgroundProvider.kt
index 3d8ced1..a1536e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/layout/LetterboxBackgroundProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/layout/LetterboxBackgroundProvider.kt
@@ -23,7 +23,6 @@
 import android.os.RemoteException
 import android.view.IWindowManager
 import com.android.systemui.CoreStartable
-import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -40,7 +39,7 @@
     @Background private val backgroundExecutor: Executor,
     private val wallpaperManager: WallpaperManager,
     @Main private val mainHandler: Handler,
-) : CoreStartable, Dumpable {
+) : CoreStartable {
     @ColorInt
     var letterboxBackgroundColor: Int = Color.BLACK
         private set
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 f52b924..df8fb5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -91,7 +91,7 @@
                     ImageSpan(it, ImageSpan.ALIGN_CENTER)
                 }
             val decoratedSummary =
-                SpannableString("x" + entry.ranking.summarization).apply {
+                SpannableString("x " + entry.ranking.summarization).apply {
                     setSpan(
                         /* what = */ imageSpan,
                         /* start = */ 0,
@@ -100,9 +100,9 @@
                     )
                     entry.ranking.summarization?.let {
                         setSpan(
-                            /* what = */ StyleSpan(Typeface.BOLD),
-                            /* start = */ 1,
-                            /* end = */ it.length,
+                            /* what = */ StyleSpan(Typeface.ITALIC),
+                            /* start = */ 2,
+                            /* end = */ it.length + 2,
                             /* flags = */ Spanned.SPAN_EXCLUSIVE_INCLUSIVE,
                         )
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index ccfb43e..4053d06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -25,6 +25,7 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.wm.shell.bubbles.Bubbles;
 
 import java.util.Optional;
@@ -99,8 +100,14 @@
         row.setJustClicked(true);
         DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
 
-        if (!row.getEntry().isBubble() && mBubblesOptional.isPresent()) {
-            mBubblesOptional.get().collapseStack();
+        if (NotificationBundleUi.isEnabled()) {
+            if (!row.getEntryAdapter().isBubbleCapable() && mBubblesOptional.isPresent()) {
+                mBubblesOptional.get().collapseStack();
+            }
+        } else {
+            if (!row.getEntry().isBubble() && mBubblesOptional.isPresent()) {
+                mBubblesOptional.get().collapseStack();
+            }
         }
 
         mNotificationActivityStarter.onNotificationClicked(entry, row);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
index c79cae7..6dd44a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
@@ -24,6 +24,7 @@
 import android.app.Notification;
 import android.content.Context;
 import android.os.Build;
+import android.service.notification.StatusBarNotification;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -163,6 +164,48 @@
             // TODO(b/396446620): implement bundle icons
             return null;
         }
+
+        @Override
+        public boolean isColorized() {
+            return false;
+        }
+
+        @Override
+        @Nullable
+        public StatusBarNotification getSbn() {
+            return null;
+        }
+
+        @Override
+        public boolean canDragAndDrop() {
+            return false;
+        }
+
+        @Override
+        public boolean isBubbleCapable() {
+            return false;
+        }
+
+        @Override
+        @Nullable
+        public String getStyle() {
+            return null;
+        }
+
+        @Override
+        public int getSectionBucket() {
+            return mBucket;
+        }
+
+        @Override
+        public boolean isAmbient() {
+            return false;
+        }
+
+        @Override
+        public boolean isFullScreenCapable() {
+            return false;
+        }
     }
 
     public static final List<BundleEntry> ROOT_BUNDLES = List.of(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index 109ebe6..307a957 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.collection;
 
 import android.content.Context;
+import android.service.notification.StatusBarNotification;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -109,4 +110,30 @@
      * Returns whether the content of this entry is sensitive
      */
     StateFlow<Boolean> isSensitive();
+
+    /**
+     * Returns whether this row has a background color set by an app
+     */
+    boolean isColorized();
+
+    /**
+     * Returns the SBN that backs this row, if present
+     */
+    @Nullable
+    StatusBarNotification getSbn();
+
+    boolean canDragAndDrop();
+
+    boolean isBubbleCapable();
+
+    @Nullable String getStyle();
+
+    int getSectionBucket();
+
+    boolean isAmbient();
+
+    default boolean isFullScreenCapable() {
+        return false;
+    }
 }
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index fb2a66c..b19ba3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -32,7 +32,6 @@
 import static com.android.systemui.statusbar.notification.collection.BundleEntry.ROOT_BUNDLES;
 import static com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY;
 import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_ALERTING;
 
 import static java.util.Objects.requireNonNull;
 
@@ -42,6 +41,7 @@
 import android.app.Notification.MessagingStyle.Message;
 import android.app.NotificationChannel;
 import android.app.NotificationManager.Policy;
+import android.app.PendingIntent;
 import android.app.Person;
 import android.app.RemoteInput;
 import android.app.RemoteInputHistoryItem;
@@ -79,7 +79,6 @@
 import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel;
 import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
-import com.android.systemui.statusbar.notification.stack.PriorityBucket;
 import com.android.systemui.util.ListenerSet;
 
 import kotlinx.coroutines.flow.MutableStateFlow;
@@ -183,7 +182,6 @@
             new ListenerSet<>();
 
     private boolean mPulseSupressed;
-    private int mBucket = BUCKET_ALERTING;
     private boolean mIsMarkedForUserTriggeredMovement;
     private boolean mIsHeadsUpEntry;
 
@@ -353,6 +351,56 @@
         public IconPack getIcons() {
             return NotificationEntry.this.getIcons();
         }
+
+        @Override
+        public boolean isColorized() {
+            return getSbn().getNotification().isColorized();
+        }
+
+        @Override
+        @Nullable
+        public StatusBarNotification getSbn() {
+            return NotificationEntry.this.getSbn();
+        }
+
+        @Override
+        public boolean canDragAndDrop() {
+            boolean canBubble = canBubble();
+            Notification notif = getSbn().getNotification();
+            PendingIntent dragIntent = notif.contentIntent != null ? notif.contentIntent
+                    : notif.fullScreenIntent;
+            if (dragIntent != null && dragIntent.isActivity() && !canBubble) {
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public boolean isBubbleCapable() {
+            return NotificationEntry.this.isBubble();
+        }
+
+        @Override
+        @Nullable
+        public String getStyle() {
+            return getNotificationStyle();
+        }
+
+        @Override
+        public int getSectionBucket() {
+            return mBucket;
+        }
+
+        @Override
+        public boolean isAmbient() {
+            return mRanking.isAmbient();
+        }
+
+        @Override
+        public boolean isFullScreenCapable() {
+            return getSbn().getNotification().fullScreenIntent != null;
+        }
+
     }
 
     public EntryAdapter getEntryAdapter() {
@@ -560,15 +608,6 @@
         return wasBubble != isBubble();
     }
 
-    @PriorityBucket
-    public int getBucket() {
-        return mBucket;
-    }
-
-    public void setBucket(@PriorityBucket int bucket) {
-        mBucket = bucket;
-    }
-
     public ExpandableNotificationRow getRow() {
         return row;
     }
@@ -589,25 +628,45 @@
     /**
      * Get the children that are actually attached to this notification's row.
      *
-     * TODO: Seems like most callers here should probably be using
-     * {@link GroupMembershipManager#getChildren(PipelineEntry)}
+     * TODO: Seems like most callers here should be asking a PipelineEntry, not a NotificationEntry
      */
     public @Nullable List<NotificationEntry> getAttachedNotifChildren() {
-        if (row == null) {
-            return null;
+        if (NotificationBundleUi.isEnabled()) {
+            if (isGroupSummary()) {
+                return ((GroupEntry) getParent()).getChildren();
+            }
+        } else {
+            if (row == null) {
+                return null;
+            }
+
+            List<ExpandableNotificationRow> rowChildren = row.getAttachedChildren();
+            if (rowChildren == null) {
+                return null;
+            }
+
+            ArrayList<NotificationEntry> children = new ArrayList<>();
+            for (ExpandableNotificationRow child : rowChildren) {
+                children.add(child.getEntry());
+            }
+
+            return children;
+        }
+        return null;
+    }
+
+    private boolean isGroupSummary() {
+        if (getParent() == null) {
+            // The entry is not attached, so it doesn't count.
+            return false;
+        }
+        PipelineEntry pipelineEntry = getParent();
+        if (!(pipelineEntry instanceof GroupEntry groupEntry)) {
+            return false;
         }
 
-        List<ExpandableNotificationRow> rowChildren = row.getAttachedChildren();
-        if (rowChildren == null) {
-            return null;
-        }
-
-        ArrayList<NotificationEntry> children = new ArrayList<>();
-        for (ExpandableNotificationRow child : rowChildren) {
-            children.add(child.getEntry());
-        }
-
-        return children;
+        // If entry is a summary, its parent is a GroupEntry with summary = entry.
+        return groupEntry.getSummary() == this;
     }
 
     public void notifyFullScreenIntentLaunched() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
index 78652cc..84de77b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
@@ -16,10 +16,13 @@
 
 package com.android.systemui.statusbar.notification.collection;
 
+import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_ALERTING;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
+import com.android.systemui.statusbar.notification.stack.PriorityBucket;
 
 /**
  * Class to represent a notification, group, or bundle in the pipeline.
@@ -29,6 +32,7 @@
     final String mKey;
     final ListAttachState mAttachState = ListAttachState.create();
     final ListAttachState mPreviousAttachState = ListAttachState.create();
+    protected int mBucket = BUCKET_ALERTING;
 
     public PipelineEntry(String key) {
         this.mKey = key;
@@ -86,4 +90,13 @@
     final ListAttachState getPreviousAttachState() {
         return mPreviousAttachState;
     }
+
+    @PriorityBucket
+    public int getBucket() {
+        return mBucket;
+    }
+
+    public void setBucket(@PriorityBucket int bucket) {
+        mBucket = bucket;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimator.kt
new file mode 100644
index 0000000..f9bd805
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimator.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.headsup
+
+import android.content.Context
+import com.android.systemui.res.R
+
+/**
+ * A class shared between [StackScrollAlgorithm] and [StackStateAnimator] to ensure all heads up
+ * animations use the same animation values.
+ */
+class HeadsUpAnimator(context: Context) {
+    init {
+        NotificationsHunSharedAnimationValues.assertInNewMode()
+    }
+
+    var headsUpAppearHeightBottom: Int = 0
+    var stackTopMargin: Int = 0
+
+    private var headsUpAppearStartAboveScreen = context.fetchHeadsUpAppearStartAboveScreen()
+
+    /**
+     * Returns the Y translation for a heads-up notification animation.
+     *
+     * For an appear animation, the returned Y translation should be the starting value of the
+     * animation. For a disappear animation, the returned Y translation should be the ending value
+     * of the animation.
+     */
+    fun getHeadsUpYTranslation(isHeadsUpFromBottom: Boolean): Int {
+        NotificationsHunSharedAnimationValues.assertInNewMode()
+
+        if (isHeadsUpFromBottom) {
+            // start from or end at the bottom of the screen
+            return headsUpAppearHeightBottom + headsUpAppearStartAboveScreen
+        }
+
+        // start from or end at the top of the screen
+        return -stackTopMargin - headsUpAppearStartAboveScreen
+    }
+
+    /** Should be invoked when resource values may have changed. */
+    fun updateResources(context: Context) {
+        headsUpAppearStartAboveScreen = context.fetchHeadsUpAppearStartAboveScreen()
+    }
+
+    private fun Context.fetchHeadsUpAppearStartAboveScreen(): Int {
+        return this.resources.getDimensionPixelSize(R.dimen.heads_up_appear_y_above_screen)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt
new file mode 100644
index 0000000..ca9d498
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.headsup
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the notifications hun shared animation values flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object NotificationsHunSharedAnimationValues {
+    /** The aconfig flag name */
+    const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_HUN_SHARED_ANIMATION_VALUES
+
+    /** A token used for dependency declaration */
+    val token: FlagToken
+        get() = FlagToken(FLAG_NAME, isEnabled)
+
+    /** Is the refactor enabled */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.notificationsHunSharedAnimationValues()
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This protects users from the
+     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+     * build to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun isUnexpectedlyInLegacyMode() =
+        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This will throw an exception if
+     * the flag is not enabled to ensure that the refactor author catches issues in testing.
+     * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
+     */
+    @JvmStatic
+    inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * the flag is enabled to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
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 185e7fa..179951f 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
@@ -974,6 +974,7 @@
         } else if (isAboveShelf() != wasAboveShelf) {
             mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
         }
+        updateBackgroundOpacity();
     }
 
     /**
@@ -1678,10 +1679,15 @@
             view.setBackgroundTintColor(color);
         }
         if (notificationRowTransparency()
-                && (mBackgroundNormal != null)
-                && (mEntry != null)) {
-            mBackgroundNormal.setBgIsColorized(
-                    mEntry.getSbn().getNotification().isColorized());
+                && (mBackgroundNormal != null)) {
+            if (NotificationBundleUi.isEnabled()) {
+                mBackgroundNormal.setBgIsColorized(mEntryAdapter.isColorized());
+            } else {
+                if (mEntry != null) {
+                    mBackgroundNormal.setBgIsColorized(
+                            mEntry.getSbn().getNotification().isColorized());
+                }
+            }
         }
     }
 
@@ -2205,7 +2211,7 @@
                     R.dimen.notification_min_height);
         }
         mMaxSmallHeightWithSummarization = NotificationUtils.getFontScaledHeight(mContext,
-                com.android.internal.R.dimen.notification_min_height);
+                com.android.internal.R.dimen.notification_collapsed_height_with_summarization);
         mMaxExpandedHeight = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_max_height);
         mMaxExpandedHeightForPromotedOngoing = NotificationUtils.getFontScaledHeight(mContext,
@@ -2373,7 +2379,11 @@
             return traceTag;
         }
 
-        return traceTag + "(" + getEntry().getNotificationStyle() + ")";
+        if (NotificationBundleUi.isEnabled()) {
+            return traceTag + "(" + getEntryAdapter().getStyle() + ")";
+        } else {
+            return traceTag + "(" + getEntry().getNotificationStyle() + ")";
+        }
     }
 
     @Override
@@ -3067,6 +3077,7 @@
                     mChildrenContainer.setOnKeyguard(onKeyguard);
                 }
             }
+            updateBackgroundOpacity();
         }
     }
 
@@ -3696,8 +3707,14 @@
             return true;
         }
         // The colorized background is another layer with which all other elements overlap
-        if (getEntry().getSbn().getNotification().isColorized()) {
-            return true;
+        if (NotificationBundleUi.isEnabled()) {
+            if (mEntryAdapter.isColorized()) {
+                return true;
+            }
+        } else {
+            if (getEntry().getSbn().getNotification().isColorized()) {
+                return true;
+            }
         }
         // Check if the showing layout has a need for overlapping rendering.
         // NOTE: We could check both public and private layouts here, but becuause these states
@@ -4506,4 +4523,12 @@
             return getEntry().isExpandAnimationRunning();
         }
     }
+
+    private void updateBackgroundOpacity() {
+        if (mBackgroundNormal != null) {
+            // Row background should be opaque when it's displayed as a heads-up notification or
+            // displayed on keyguard.
+            mBackgroundNormal.setForceOpaque(mIsHeadsUp || mOnKeyguard);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 07711b6..02e8f49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -127,7 +127,14 @@
                 @Override
                 public void onSettingChanged(Uri setting, int userId, String value) {
                     if (BUBBLES_SETTING_URI.equals(setting)) {
-                        final int viewUserId = mView.getEntry().getSbn().getUserId();
+                        if (NotificationBundleUi.isEnabled()
+                                && mView.getEntryAdapter().getSbn() == null) {
+                            // only valid for notification rows
+                            return;
+                        }
+                        final int viewUserId = NotificationBundleUi.isEnabled()
+                            ? mView.getEntryAdapter().getSbn().getUserId()
+                            : mView.getEntry().getSbn().getUserId();
                         if (viewUserId == UserHandle.USER_ALL || viewUserId == userId) {
                             mView.getPrivateLayout().setBubblesEnabledForUser(
                                     BUBBLES_SETTING_ENABLED_VALUE.equals(value));
@@ -376,8 +383,12 @@
             public void onViewAttachedToWindow(View v) {
                 if (NotificationBundleUi.isEnabled()) {
                     mView.setInitializationTime(mClock.elapsedRealtime());
+                    if (mView.getEntryAdapter().getSbn() != null) {
+                        mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
+                    }
                 } else {
                     mView.getEntry().setInitializationTime(mClock.elapsedRealtime());
+                    mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
                 }
                 mPluginManager.addPluginListener(mView,
                         NotificationMenuRowPlugin.class, false /* Allow multiple */);
@@ -385,7 +396,7 @@
                     mView.setOnKeyguard(mStatusBarStateController.getState() == KEYGUARD);
                     mStatusBarStateController.addCallback(mStatusBarStateListener);
                 }
-                mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
+
             }
 
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
index d5551b1..9ae2eb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
@@ -54,6 +54,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 
 import javax.inject.Inject;
 
@@ -100,7 +101,15 @@
             enr = (ExpandableNotificationRow) view;
         }
 
-        StatusBarNotification sn = enr.getEntry().getSbn();
+        if (NotificationBundleUi.isEnabled()) {
+            if (!enr.getEntryAdapter().canDragAndDrop()) {
+                return;
+            }
+        }
+
+        StatusBarNotification sn = NotificationBundleUi.isEnabled()
+                ? enr.getEntryAdapter().getSbn()
+                : enr.getEntry().getSbn();
         Notification notification = sn.getNotification();
         final PendingIntent contentIntent = notification.contentIntent != null
                 ? notification.contentIntent
@@ -115,8 +124,7 @@
                  .show();
             return;
         }
-        Bitmap iconBitmap = getBitmapFromDrawable(
-                getPkgIcon(enr.getEntry().getSbn().getPackageName()));
+        Bitmap iconBitmap = getBitmapFromDrawable(getPkgIcon(sn.getPackageName()));
 
         final ImageView snapshot = new ImageView(mContext);
         snapshot.setImageBitmap(iconBitmap);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
index 344d0f6..ba80f01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -51,6 +51,7 @@
  */
 public class HybridConversationNotificationView extends HybridNotificationView {
 
+    private static final int MAX_SUMMARIZATION_LINES = 2;
     private ImageView mConversationIconView;
     private TextView mConversationSenderName;
     private ViewStub mConversationFacePileStub;
@@ -292,11 +293,14 @@
             @Nullable CharSequence summarization
     ) {
         if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) return;
-        if (summarization != null) {
+        if (!TextUtils.isEmpty(summarization)) {
             mConversationSenderName.setVisibility(GONE);
             titleText = null;
             contentText = summarization;
+            mTextView.setSingleLine(false);
+            mTextView.setMaxLines(MAX_SUMMARIZATION_LINES);
         } else {
+            mTextView.setSingleLine(true);
             if (conversationSenderName == null) {
                 mConversationSenderName.setVisibility(GONE);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index c0bc132..4978fa4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -53,6 +53,7 @@
 public class NotificationBackgroundView extends View implements Dumpable,
         ExpandableNotificationRow.DismissButtonTargetVisibilityListener {
 
+    private static final int MAX_ALPHA = 0xFF;
     private final boolean mDontModifyCorners;
     private Drawable mBackground;
     private int mClipTopAmount;
@@ -74,6 +75,7 @@
     private final ColorStateList mDarkColoredStatefulColors;
     private final int mNormalColor;
     private boolean mBgIsColorized = false;
+    private boolean mForceOpaque = false;
     private final int convexR = 9;
     private final int concaveR = 22;
 
@@ -156,6 +158,14 @@
         mBgIsColorized = b;
     }
 
+    /** Sets if the background should be opaque. */
+    public void setForceOpaque(boolean forceOpaque) {
+        mForceOpaque = forceOpaque;
+        if (notificationRowTransparency()) {
+            updateBaseLayerColor();
+        }
+    }
+
     private Path calculateDismissButtonCutoutPath(Rect backgroundBounds) {
         // TODO(b/365585705): Adapt to RTL after the UX design is finalized.
 
@@ -317,11 +327,15 @@
         // Instead, we set a color filter that essentially replaces every pixel of the drawable.
         // For non-colorized notifications, this function specifies a new color token.
         // For colorized notifications, this uses a color that matches the tint color at 90% alpha.
+        int color = isColorized()
+                ? ColorUtils.setAlphaComponent(mTintColor, (int) (MAX_ALPHA * 0.9f))
+                : SurfaceEffectColors.surfaceEffect1(getContext());
+        if (mForceOpaque) {
+            color = ColorUtils.setAlphaComponent(color, MAX_ALPHA);
+        }
         getBaseBackgroundLayer().setColorFilter(
                 new PorterDuffColorFilter(
-                        isColorized()
-                                ? ColorUtils.setAlphaComponent(mTintColor, (int) (255 * 0.9f))
-                                : SurfaceEffectColors.surfaceEffect1(getContext()),
+                        color,
                         PorterDuff.Mode.SRC)); // SRC operator discards the drawable's color+alpha
     }
 
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 7444679..daa598b 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
@@ -70,6 +70,7 @@
 import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder;
 import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleLineViewModel;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
@@ -468,7 +469,10 @@
                 if (LockscreenOtpRedaction.isEnabled()
                         && bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
                     result.newPublicView = createSensitiveContentMessageNotification(
-                            row.getEntry().getSbn().getNotification(), builder.getStyle(),
+                            NotificationBundleUi.isEnabled()
+                                    ? row.getEntryAdapter().getSbn().getNotification()
+                                    : row.getEntry().getSbn().getNotification(),
+                            builder.getStyle(),
                             systemUiContext, packageContext).createContentView();
                 } else {
                     result.newPublicView = builder.makePublicContentView(bindParams.isMinimized);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 0d29981..1932037 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -64,6 +64,7 @@
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
@@ -594,12 +595,11 @@
         if (mContainingNotification == null) {
             return null;
         }
-        final NotificationEntry entry = mContainingNotification.getEntry();
-        if (entry == null) {
-            return null;
+        if (NotificationBundleUi.isEnabled()) {
+            return mContainingNotification.getEntryAdapter().getSbn();
+        } else {
+            return mContainingNotification.getEntry().getSbn();
         }
-
-        return entry.getSbn();
     }
 
     /**
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 c930dd8..3586078 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
@@ -16,6 +16,7 @@
 package com.android.systemui.statusbar.notification.row
 
 import android.annotation.SuppressLint
+import android.app.Flags
 import android.app.Notification
 import android.app.Notification.EXTRA_SUMMARIZED_CONTENT
 import android.app.Notification.MessagingStyle
@@ -49,10 +50,10 @@
 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.EntryAdapter
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.logKey
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
 import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED
 import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED
@@ -1521,6 +1522,10 @@
                 entry.promotedNotificationContentModel = result.promotedContent
             }
 
+            if (PromotedNotificationUiForceExpanded.isEnabled) {
+                row.setPromotedOngoing(entry.isOngoingPromoted())
+            }
+
             result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) }
 
             setContentViewsFromRemoteViews(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
index bb4aa86..4082a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.NotifRemoteViewsFactory
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
 import javax.inject.Inject
 
 /**
@@ -60,7 +61,18 @@
         row: ExpandableNotificationRow,
         context: Context,
     ): NotificationIconProvider {
-        val sbn = row.entry.sbn
+        val sbn = if (NotificationBundleUi.isEnabled) row.entryAdapter?.sbn else  row.entry.sbn
+        if (sbn == null) {
+            return object : NotificationIconProvider {
+                override fun shouldShowAppIcon(): Boolean {
+                    return false
+                }
+
+                override fun getAppIcon(): Drawable? {
+                    return null
+                }
+            }
+        }
         return object : NotificationIconProvider {
             override fun shouldShowAppIcon(): Boolean {
                 val shouldShowAppIcon = iconStyleProvider.shouldShowAppIcon(sbn, context)
@@ -68,7 +80,7 @@
                 return shouldShowAppIcon
             }
 
-            override fun getAppIcon(): Drawable {
+            override fun getAppIcon(): Drawable? {
                 val withWorkProfileBadge =
                     iconStyleProvider.shouldShowWorkProfileBadge(sbn, context)
                 return appIconProvider.getOrFetchAppIcon(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index 990adf7..f492b25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -30,6 +30,7 @@
 import com.android.systemui.statusbar.notification.ImageTransformState;
 import com.android.systemui.statusbar.notification.row.BigPictureIconManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 
 /**
  * Wraps a notification containing a big picture template
@@ -47,7 +48,9 @@
     public void onContentUpdated(ExpandableNotificationRow row) {
         super.onContentUpdated(row);
         resolveViews();
-        updateImageTag(row.getEntry().getSbn());
+        updateImageTag(NotificationBundleUi.isEnabled()
+                ? row.getEntryAdapter().getSbn()
+                : row.getEntry().getSbn());
     }
 
     private void resolveViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
index d58c183..dec674c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
@@ -23,6 +23,7 @@
 import com.android.internal.widget.ImageFloatingTextView;
 import com.android.systemui.statusbar.TransformableView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 
 /**
  * Wraps a notification containing a big text template
@@ -44,7 +45,9 @@
     public void onContentUpdated(ExpandableNotificationRow row) {
         // Reinspect the notification. Before the super call, because the super call also updates
         // the transformation types and we need to have our values set by then.
-        resolveViews(row.getEntry().getSbn());
+        resolveViews(NotificationBundleUi.isEnabled()
+                ? row.getEntryAdapter().getSbn()
+                : row.getEntry().getSbn());
         super.onContentUpdated(row);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index e9eecdd8..585051a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -52,6 +52,7 @@
 import com.android.systemui.statusbar.notification.TransformState;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.shared.NotificationAddXOnHoverToDismiss;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 
 import java.util.Stack;
 
@@ -222,7 +223,9 @@
     @Override
     public void onContentUpdated(ExpandableNotificationRow row) {
         super.onContentUpdated(row);
-        mIsLowPriority = row.getEntry().isAmbient();
+        mIsLowPriority = NotificationBundleUi.isEnabled()
+                ? row.getEntryAdapter().isAmbient()
+                : row.getEntry().isAmbient();
         mTransformLowPriorityTitle = !row.isChildInGroup() && !row.isSummaryWithChildren();
         ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews();
 
@@ -231,7 +234,9 @@
         updateTransformedTypes();
         addRemainingTransformTypes();
         updateCropToPaddingForImageViews();
-        Notification n = row.getEntry().getSbn().getNotification();
+        Notification n = NotificationBundleUi.isEnabled()
+                ? row.getEntryAdapter().getSbn().getNotification()
+                : row.getEntry().getSbn().getNotification();
         mIcon.setTag(ImageTransformState.ICON_TAG, n.getSmallIcon());
 
         // We need to reset all views that are no longer transforming in case a view was previously
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index b9aa571..4146a94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -54,6 +54,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.HybridNotificationView;
 import com.android.systemui.util.DimensionKt;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 
 import java.util.function.Consumer;
 
@@ -310,7 +311,9 @@
     public void onContentUpdated(ExpandableNotificationRow row) {
         // Reinspect the notification. Before the super call, because the super call also updates
         // the transformation types and we need to have our values set by then.
-        resolveTemplateViews(row.getEntry().getSbn());
+        resolveTemplateViews(NotificationBundleUi.isEnabled()
+                ? row.getEntryAdapter().getSbn()
+                : row.getEntry().getSbn());
         super.onContentUpdated(row);
         // With the modern templates, a large icon visually overlaps the header, so we can't
         // hide the header, we must show it.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 3987ca6..64babb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -80,7 +80,10 @@
                 return new NotificationProgressTemplateViewWrapper(ctx, v, row);
             }
 
-            if (row.getEntry().getSbn().getNotification().isStyle(
+            if (NotificationBundleUi.isEnabled()
+                    ? row.getEntryAdapter().getSbn().getNotification().isStyle(
+                    Notification.DecoratedCustomViewStyle.class)
+                    : row.getEntry().getSbn().getNotification().isStyle(
                     Notification.DecoratedCustomViewStyle.class)) {
                 return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 048958e..e830d18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -60,6 +60,7 @@
 import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -421,7 +422,12 @@
         Trace.beginSection("NotifChildCont#recreateHeader");
         mHeaderClickListener = listener;
         mIsConversation = isConversation;
-        StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
+        StatusBarNotification notification = NotificationBundleUi.isEnabled()
+                ? mContainingNotification.getEntryAdapter().getSbn()
+                : mContainingNotification.getEntry().getSbn();
+        if (notification == null) {
+            return;
+        }
         final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
                 notification.getNotification());
         Trace.beginSection("recreateHeader#makeNotificationGroupHeader");
@@ -565,7 +571,12 @@
     void recreateLowPriorityHeader(Notification.Builder builder) {
         AsyncGroupHeaderViewInflation.assertInLegacyMode();
         RemoteViews header;
-        StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
+        StatusBarNotification notification = NotificationBundleUi.isEnabled()
+                ? mContainingNotification.getEntryAdapter().getSbn()
+                : mContainingNotification.getEntry().getSbn();
+        if (notification == null) {
+            return;
+        }
         if (mIsMinimized) {
             if (builder == null) {
                 builder = Notification.Builder.recoverBuilder(getContext(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 3d8fe01..96f0e6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.statusbar.notification.dagger.SocialHeader
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
 import com.android.systemui.statusbar.notification.stack.PriorityBucket
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -164,7 +165,9 @@
             view === socialHeaderView -> BUCKET_SOCIAL
             view === recsHeaderView -> BUCKET_RECS
             view === promoHeaderView -> BUCKET_PROMO
-            view is ExpandableNotificationRow -> view.entry.bucket
+            view is ExpandableNotificationRow ->
+                if (NotificationBundleUi.isEnabled) view.entryAdapter?.sectionBucket
+                else view.entry.bucket
             else -> null
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index e7a77eb..4390f1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -117,8 +117,10 @@
 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix;
 import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator;
 import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil;
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -233,6 +235,8 @@
     private String mLastInitViewDumpString;
     private long mLastInitViewElapsedRealtime;
 
+    @Nullable
+    private final HeadsUpAnimator mHeadsUpAnimator;
     /**
      * The algorithm which calculates the properties for our children
      */
@@ -668,8 +672,13 @@
         mExpandHelper.setEventSource(this);
         mExpandHelper.setScrollAdapter(mScrollAdapter);
 
-        mStackScrollAlgorithm = createStackScrollAlgorithm(context);
-        mStateAnimator = new StackStateAnimator(context, this);
+        if (NotificationsHunSharedAnimationValues.isEnabled()) {
+            mHeadsUpAnimator = new HeadsUpAnimator(context);
+        } else {
+            mHeadsUpAnimator = null;
+        }
+        mStackScrollAlgorithm =  new StackScrollAlgorithm(context, this, mHeadsUpAnimator);
+        mStateAnimator = new StackStateAnimator(context, this, mHeadsUpAnimator);
         setOutlineProvider(mOutlineProvider);
 
         // We could set this whenever we 'requestChildUpdate' much like the viewTreeObserver, but
@@ -1024,12 +1033,18 @@
                         || !(view instanceof ExpandableNotificationRow row)) {
                     continue;
                 }
+                int bucket = NotificationBundleUi.isEnabled()
+                        ? row.getEntryAdapter().getSectionBucket()
+                        : row.getEntry().getBucket();
+                boolean isAmbient = NotificationBundleUi.isEnabled()
+                        ? row.getEntryAdapter().isAmbient()
+                        : row.getEntry().isAmbient();
                 currentIndex++;
                 boolean beforeSpeedBump;
                 if (mHighPriorityBeforeSpeedBump) {
-                    beforeSpeedBump = row.getEntry().getBucket() < BUCKET_SILENT;
+                    beforeSpeedBump = bucket < BUCKET_SILENT;
                 } else {
-                    beforeSpeedBump = !row.getEntry().isAmbient();
+                    beforeSpeedBump = !isAmbient;
                 }
                 if (beforeSpeedBump) {
                     speedBumpIndex = currentIndex;
@@ -3582,10 +3597,6 @@
         mGoToFullShadeNeedsAnimation = false;
     }
 
-    protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
-        return new StackScrollAlgorithm(context, this);
-    }
-
     /**
      * @return Whether a y coordinate is inside the content.
      */
@@ -5111,9 +5122,16 @@
     public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
         SceneContainerFlag.assertInLegacyMode();
         mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
-        mStackScrollAlgorithm.setHeadsUpAppearHeightBottom(height);
-        mStateAnimator.setHeadsUpAppearHeightBottom(height);
-        mStateAnimator.setStackTopMargin(mAmbientState.getStackTopMargin());
+
+        if (NotificationsHunSharedAnimationValues.isEnabled()) {
+            mHeadsUpAnimator.setHeadsUpAppearHeightBottom(height);
+            mHeadsUpAnimator.setStackTopMargin(mAmbientState.getStackTopMargin());
+        } else {
+            mStackScrollAlgorithm.setHeadsUpAppearHeightBottom(height);
+            mStateAnimator.setHeadsUpAppearHeightBottom(height);
+            mStateAnimator.setStackTopMargin(mAmbientState.getStackTopMargin());
+        }
+
         requestChildrenUpdate();
     }
 
@@ -6423,13 +6441,16 @@
     static boolean matchesSelection(
             ExpandableNotificationRow row,
             @SelectedRows int selection) {
+        int bucket = NotificationBundleUi.isEnabled()
+                ? row.getEntryAdapter().getSectionBucket()
+                : row.getEntry().getBucket();
         switch (selection) {
             case ROWS_ALL:
                 return true;
             case ROWS_HIGH_PRIORITY:
-                return row.getEntry().getBucket() < BUCKET_SILENT;
+                return bucket < BUCKET_SILENT;
             case ROWS_GENTLE:
-                return row.getEntry().getBucket() == BUCKET_SILENT;
+                return bucket == BUCKET_SILENT;
             default:
                 throw new IllegalArgumentException("Unknown selection: " + selection);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 4459430..124e6f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -122,6 +122,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationSnooze;
 import com.android.systemui.statusbar.notification.shared.GroupHunAnimationFix;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -639,8 +640,10 @@
                     mView.onSwipeEnd();
                     if (animView instanceof ExpandableNotificationRow row) {
                         if (row.isPinned() && !canChildBeDismissed(row)
-                                && row.getEntry().getSbn().getNotification().fullScreenIntent
-                                == null) {
+                                && NotificationBundleUi.isEnabled()
+                                ? !row.getEntryAdapter().isFullScreenCapable()
+                                : (row.getEntry().getSbn().getNotification().fullScreenIntent
+                                        == null)) {
                             mHeadsUpManager.removeNotification(
                                     row.getKey(),
                                     /* removeImmediately= */ true,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 08bc8f5..4e91680 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
 import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
 import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.Compile
@@ -407,8 +408,14 @@
                 }
 
             if (counter != null) {
-                val entry = (currentNotification as? ExpandableNotificationRow)?.entry
-                counter.incrementForBucket(entry?.bucket)
+                if (NotificationBundleUi.isEnabled) {
+                    val entry = (currentNotification as? ExpandableNotificationRow)?.entry
+                    counter.incrementForBucket(entry?.bucket)
+                } else {
+                    val entryAdapter =
+                        (currentNotification as? ExpandableNotificationRow)?.entryAdapter
+                    counter.incrementForBucket(entryAdapter?.sectionBucket)
+                }
             }
 
             log {
@@ -461,12 +468,15 @@
         val height = view.heightWithoutLockscreenConstraints.toFloat()
         val gapAndDividerHeight =
             calculateGapAndDividerHeight(stack, previousView, current = view, visibleIndex)
+        val canPeek = view is ExpandableNotificationRow &&
+                if (NotificationBundleUi.isEnabled) view.entryAdapter?.canPeek() == true
+                else view.entry.isStickyAndNotDemoted
 
         var size =
             if (onLockscreen) {
                 if (
                     view is ExpandableNotificationRow &&
-                        (view.entry.isStickyAndNotDemoted ||
+                        (canPeek ||
                             (PromotedNotificationUiForceExpanded.isEnabled &&
                                 view.isPromotedOngoing))
                 ) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 88d3ad8..4effb76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -36,9 +36,12 @@
 import com.android.systemui.statusbar.notification.SourceType;
 import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator;
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.statusbar.notification.shared.NotificationHeadsUpCycling;
 
 import java.util.ArrayList;
@@ -56,6 +59,9 @@
     private static final String TAG = "StackScrollAlgorithm";
     private static final SourceType STACK_SCROLL_ALGO = SourceType.from("StackScrollAlgorithm");
     private final ViewGroup mHostView;
+    @Nullable
+    private final HeadsUpAnimator mHeadsUpAnimator;
+
     private float mPaddingBetweenElements;
     private float mGapHeight;
     private float mGapHeightOnLockscreen;
@@ -78,8 +84,12 @@
     private int mHeadsUpAppearHeightBottom;
     private int mHeadsUpCyclingPadding;
 
-    public StackScrollAlgorithm(Context context, ViewGroup hostView) {
+    public StackScrollAlgorithm(
+            Context context,
+            ViewGroup hostView,
+            @Nullable HeadsUpAnimator headsUpAnimator) {
         mHostView = hostView;
+        mHeadsUpAnimator = headsUpAnimator;
         initView(context);
     }
 
@@ -111,6 +121,9 @@
         mQuickQsOffsetHeight = SystemBarUtils.getQuickQsOffsetHeight(context);
         mSmallCornerRadius = res.getDimension(R.dimen.notification_corner_radius_small);
         mLargeCornerRadius = res.getDimension(R.dimen.notification_corner_radius);
+        if (NotificationsHunSharedAnimationValues.isEnabled()) {
+            mHeadsUpAnimator.updateResources(context);
+        }
     }
 
     /**
@@ -250,6 +263,7 @@
     }
 
     public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
+        NotificationsHunSharedAnimationValues.assertInLegacyMode();
         mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
     }
 
@@ -911,7 +925,9 @@
                 if (SceneContainerFlag.isEnabled()) {
                     if (shouldHunBeVisibleWhenScrolled(row.isHeadsUp(),
                             childState.headsUpIsVisible, row.showingPulsing(),
-                            ambientState.isOnKeyguard(), row.getEntry().isStickyAndNotDemoted())) {
+                            ambientState.isOnKeyguard(), NotificationBundleUi.isEnabled()
+                                    ? row.getEntryAdapter().canPeek()
+                                    : row.getEntry().isStickyAndNotDemoted())) {
                         // the height of this child before clamping it to the top
                         float unmodifiedChildHeight = childState.height;
                         clampHunToTop(
@@ -963,7 +979,9 @@
                 } else {
                     if (shouldHunBeVisibleWhenScrolled(row.mustStayOnScreen(),
                             childState.headsUpIsVisible, row.showingPulsing(),
-                            ambientState.isOnKeyguard(), row.getEntry().isStickyAndNotDemoted())) {
+                            ambientState.isOnKeyguard(), NotificationBundleUi.isEnabled()
+                                    ? row.getEntryAdapter().canPeek()
+                                    : row.getEntry().isStickyAndNotDemoted())) {
                         // Ensure that the heads up is always visible even when scrolled off.
                         // NSSL y starts at top of screen in non-split-shade, but below the qs
                         // offset
@@ -1037,14 +1055,22 @@
                     childState.setYTranslation(inSpaceTranslation + extraTranslation);
                     cyclingInHunHeight = -1;
                 } else if (!ambientState.isDozing()) {
-                    if (shouldHunAppearFromBottom(ambientState, childState)) {
-                        // move to the bottom of the screen
-                        childState.setYTranslation(
-                                mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen);
+                    boolean shouldHunAppearFromBottom =
+                            shouldHunAppearFromBottom(ambientState, childState);
+                    if (NotificationsHunSharedAnimationValues.isEnabled()) {
+                        int yTranslation =
+                                mHeadsUpAnimator.getHeadsUpYTranslation(shouldHunAppearFromBottom);
+                        childState.setYTranslation(yTranslation);
                     } else {
-                        // move to the top of the screen
-                        childState.setYTranslation(-ambientState.getStackTopMargin()
-                                - mHeadsUpAppearStartAboveScreen);
+                        if (shouldHunAppearFromBottom) {
+                            // move to the bottom of the screen
+                            childState.setYTranslation(
+                                    mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen);
+                        } else {
+                            // move to the top of the screen
+                            childState.setYTranslation(-ambientState.getStackTopMargin()
+                                    - mHeadsUpAppearStartAboveScreen);
+                        }
                     }
                 } else {
                     // Make sure row yTranslation is at maximum the HUN yTranslation,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 4da418e..9a5cf9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -27,6 +27,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.util.Property;
 import android.view.View;
@@ -39,6 +40,8 @@
 import com.android.systemui.shared.clocks.AnimatableClockView;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator;
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
@@ -83,6 +86,9 @@
     private final ExpandableViewState mTmpState = new ExpandableViewState();
     private final AnimationProperties mAnimationProperties;
     public NotificationStackScrollLayout mHostLayout;
+    @Nullable
+    private final HeadsUpAnimator mHeadsUpAnimator;
+
     private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
             new ArrayList<>();
     private ArrayList<View> mNewAddChildren = new ArrayList<>();
@@ -104,8 +110,12 @@
     private NotificationShelf mShelf;
     private StackStateLogger mLogger;
 
-    public StackStateAnimator(Context context, NotificationStackScrollLayout hostLayout) {
+    public StackStateAnimator(
+            Context context,
+            NotificationStackScrollLayout hostLayout,
+            @Nullable HeadsUpAnimator headsUpAnimator) {
         mHostLayout = hostLayout;
+        mHeadsUpAnimator = headsUpAnimator;
         initView(context);
         mAnimationProperties = new AnimationProperties() {
 
@@ -543,7 +553,6 @@
                 mHeadsUpAppearChildren.add(changingView);
 
                 mTmpState.copyFrom(changingView.getViewState());
-                // translate the HUN in from the top, or the bottom of the screen
                 mTmpState.setYTranslation(getHeadsUpYTranslationStart(event.headsUpFromBottom));
                 // set the height and the initial position
                 mTmpState.applyToView(changingView);
@@ -728,6 +737,10 @@
     }
 
     private float getHeadsUpYTranslationStart(boolean headsUpFromBottom) {
+        if (NotificationsHunSharedAnimationValues.isEnabled()) {
+            return mHeadsUpAnimator.getHeadsUpYTranslation(headsUpFromBottom);
+        }
+
         if (headsUpFromBottom) {
             // start from the bottom of the screen
             return mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen;
@@ -814,10 +827,12 @@
     }
 
     public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
+        NotificationsHunSharedAnimationValues.assertInLegacyMode();
         mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
     }
 
     public void setStackTopMargin(int stackTopMargin) {
+        NotificationsHunSharedAnimationValues.assertInLegacyMode();
         mStackTopMargin = stackTopMargin;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 949cb0a..b662892 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -72,7 +72,7 @@
     private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler,
     private val statusBarModeRepository: StatusBarModeRepositoryStore,
     @OngoingCallLog private val logger: LogBuffer,
-) : CallbackController<OngoingCallListener>, Dumpable, CoreStartable {
+) : CallbackController<OngoingCallListener>, CoreStartable {
     private var isFullscreen: Boolean = false
     /** Non-null if there's an active call notification. */
     private var callNotificationInfo: CallNotificationInfo? = null
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 3c53d2d..6355767 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -34,7 +34,6 @@
 import androidx.annotation.VisibleForTesting
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.systemui.CoreStartable
-import com.android.systemui.Dumpable
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -81,7 +80,7 @@
     private val wakeLockBuilder: WakeLock.Builder,
     private val systemClock: SystemClock,
     internal val tempViewUiEventLogger: TemporaryViewUiEventLogger,
-) : CoreStartable, Dumpable {
+) : CoreStartable {
     /**
      * Window layout params that will be used as a starting point for the [windowLayoutParams] of
      * all subclasses.
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt
index 4cd49d0..1e78b12 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt
@@ -26,6 +26,7 @@
 import javax.inject.Inject
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
 
 class WallpaperFocalAreaViewModel
@@ -39,25 +40,31 @@
     val wallpaperFocalAreaBounds =
         combine(
                 wallpaperFocalAreaInteractor.wallpaperFocalAreaBounds,
+                keyguardTransitionInteractor.startedKeyguardTransitionStep,
+                // Emit transition state when FINISHED instead of STARTED to avoid race with
+                // wakingup command, causing layout change command not be received.
                 keyguardTransitionInteractor
                     .transition(
                         edge = Edge.create(to = Scenes.Lockscreen),
                         edgeWithoutSceneContainer = Edge.create(to = KeyguardState.LOCKSCREEN),
                     )
-                    .filter { transitionStep ->
-                        // Should not filter by TransitionState.STARTED, it may race with
-                        // wakingup command, causing layout change command not be received.
-                        transitionStep.transitionState == TransitionState.FINISHED
-                    },
-                ::Pair,
+                    .filter { it.transitionState == TransitionState.FINISHED },
+                ::Triple,
             )
-            .map { (bounds, _) -> bounds }
+            .map { (bounds, startedStep, _) ->
+                // Avoid sending wrong bounds when transitioning from LOCKSCREEN to GONE
+                if (
+                    startedStep.to == KeyguardState.LOCKSCREEN &&
+                        startedStep.from != KeyguardState.LOCKSCREEN
+                ) {
+                    bounds
+                } else {
+                    null
+                }
+            }
+            .filterNotNull()
 
     fun setFocalAreaBounds(bounds: RectF) {
         wallpaperFocalAreaInteractor.setFocalAreaBounds(bounds)
     }
-
-    fun setTapPosition(x: Float, y: Float) {
-        wallpaperFocalAreaInteractor.setTapPosition(x, y)
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index b6c6347..0e68fce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -28,6 +28,7 @@
 import android.graphics.Rect
 import android.graphics.drawable.BitmapDrawable
 import android.hardware.biometrics.BiometricFingerprintConstants
+import android.hardware.biometrics.BiometricPrompt
 import android.hardware.biometrics.PromptContentItemBulletedText
 import android.hardware.biometrics.PromptContentView
 import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
@@ -42,6 +43,7 @@
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
 import android.view.Surface
+import android.view.accessibility.accessibilityManager
 import androidx.test.filters.SmallTest
 import com.android.app.activityTaskManager
 import com.android.keyguard.AuthInteractionProperties
@@ -200,6 +202,8 @@
         overrideResource(R.dimen.biometric_dialog_face_icon_size, mockFaceIconSize)
 
         kosmos.applicationContext = context
+        whenever(kosmos.accessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt()))
+            .thenReturn(BiometricPrompt.HIDE_DIALOG_DELAY)
 
         if (testCase.fingerprint?.isAnyUdfpsType == true) {
             kosmos.authController = authController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
index e035a02..f394c80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
@@ -16,6 +16,9 @@
 
 package com.android.systemui.media.controls.ui.viewmodel
 
+import android.icu.text.MeasureFormat
+import android.icu.util.Measure
+import android.icu.util.MeasureUnit
 import android.media.MediaMetadata
 import android.media.session.MediaController
 import android.media.session.MediaSession
@@ -34,6 +37,7 @@
 import com.android.systemui.util.concurrency.FakeRepeatableExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.util.Locale
 import org.junit.After
 import org.junit.Before
 import org.junit.Ignore
@@ -155,6 +159,7 @@
         whenever(mockController.getPlaybackState()).thenReturn(state)
         // WHEN the controller is updated
         viewModel.updateController(mockController)
+        fakeExecutor.runNextReady()
         // THEN the duration is extracted
         assertThat(viewModel.progress.value!!.duration).isEqualTo(duration)
         assertThat(viewModel.progress.value!!.enabled).isTrue()
@@ -173,6 +178,7 @@
         whenever(mockController.getMetadata()).thenReturn(metadata)
         // WHEN the controller is updated
         viewModel.updateController(mockController)
+        fakeExecutor.runNextReady()
         // THEN the duration is extracted
         assertThat(viewModel.progress.value!!.duration).isEqualTo(duration)
         assertThat(viewModel.progress.value!!.enabled).isFalse()
@@ -197,6 +203,7 @@
         whenever(mockController.getPlaybackState()).thenReturn(state)
         // WHEN the controller is updated
         viewModel.updateController(mockController)
+        fakeExecutor.runNextReady()
         // THEN the seek bar is disabled
         assertThat(viewModel.progress.value!!.enabled).isFalse()
     }
@@ -220,6 +227,7 @@
         whenever(mockController.getPlaybackState()).thenReturn(state)
         // WHEN the controller is updated
         viewModel.updateController(mockController)
+        fakeExecutor.runNextReady()
         // THEN the seek bar is disabled
         assertThat(viewModel.progress.value!!.enabled).isFalse()
     }
@@ -238,6 +246,7 @@
         whenever(mockController.getPlaybackState()).thenReturn(state)
         // WHEN the controller is updated
         viewModel.updateController(mockController)
+        fakeExecutor.runNextReady()
         // THEN the seek bar is disabled
         assertThat(viewModel.progress.value!!.enabled).isFalse()
     }
@@ -254,6 +263,7 @@
         whenever(mockController.getPlaybackState()).thenReturn(state)
         // WHEN the controller is updated
         viewModel.updateController(mockController)
+        fakeExecutor.runNextReady()
         // THEN elapsed time is captured
         assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(200.toInt())
     }
@@ -536,6 +546,7 @@
         whenever(mockController.getPlaybackState()).thenReturn(state)
         // WHEN the controller is updated
         viewModel.updateController(mockController)
+        fakeExecutor.runNextReady()
         // THEN a task is queued
         assertThat(fakeExecutor.numPending()).isEqualTo(1)
     }
@@ -551,6 +562,7 @@
         whenever(mockController.getPlaybackState()).thenReturn(state)
         // WHEN updated
         viewModel.updateController(mockController)
+        fakeExecutor.runNextReady()
         // THEN an update task is not queued
         assertThat(fakeExecutor.numPending()).isEqualTo(0)
     }
@@ -572,6 +584,7 @@
         whenever(mockController.getPlaybackState()).thenReturn(state)
         // WHEN updated
         viewModel.updateController(mockController)
+        fakeExecutor.runNextReady()
         // THEN an update task is queued
         assertThat(fakeExecutor.numPending()).isEqualTo(1)
     }
@@ -593,6 +606,7 @@
         whenever(mockController.getPlaybackState()).thenReturn(state)
         // WHEN updated
         viewModel.updateController(mockController)
+        fakeExecutor.runNextReady()
         // THEN an update task is not queued
         assertThat(fakeExecutor.numPending()).isEqualTo(0)
     }
@@ -719,6 +733,7 @@
             }
         whenever(mockController.getPlaybackState()).thenReturn(state)
         viewModel.updateController(mockController)
+        fakeExecutor.runNextReady()
         // WHEN start listening
         viewModel.listening = true
         // THEN an update task is queued
@@ -820,6 +835,7 @@
         whenever(mockController.playbackState).thenReturn(state)
         val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
         viewModel.updateController(mockController)
+        fakeExecutor.runNextReady()
         verify(mockController).registerCallback(captor.capture())
         assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(firstPosition.toInt())
 
@@ -831,8 +847,48 @@
                 build()
             }
         captor.value.onPlaybackStateChanged(secondState)
+        fakeExecutor.runNextReady()
 
         // THEN then elapsed time should be updated
         assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(secondPosition.toInt())
     }
+
+    @Test
+    fun contentDescriptionUpdated() {
+        // When there is a duration and position
+        val duration = (1.5 * 60 * 60 * 1000).toLong()
+        val metadata =
+            MediaMetadata.Builder().run {
+                putLong(MediaMetadata.METADATA_KEY_DURATION, duration)
+                build()
+            }
+        whenever(mockController.getMetadata()).thenReturn(metadata)
+
+        val elapsedTime = 3000L
+        val state =
+            PlaybackState.Builder().run {
+                setState(PlaybackState.STATE_PLAYING, elapsedTime, 1f)
+                build()
+            }
+        whenever(mockController.getPlaybackState()).thenReturn(state)
+
+        viewModel.updateController(mockController)
+        fakeExecutor.runNextReady()
+
+        // Then the content description is set
+        val result = viewModel.progress.value!!
+
+        val expectedProgress =
+            MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+                .formatMeasures(Measure(3, MeasureUnit.SECOND))
+        val expectedDuration =
+            MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+                .formatMeasures(
+                    Measure(1, MeasureUnit.HOUR),
+                    Measure(30, MeasureUnit.MINUTE),
+                    Measure(0, MeasureUnit.SECOND),
+                )
+        assertThat(result.durationDescription).isEqualTo(expectedDuration)
+        assertThat(result.elapsedTimeDescription).isEqualTo(expectedProgress)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 7101df1..2a58890 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -82,6 +82,7 @@
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -732,6 +733,9 @@
         NotificationEntry entry = mock(NotificationEntry.class);
         when(row.getEntry()).thenReturn(entry);
         when(entry.isAmbient()).thenReturn(false);
+        EntryAdapter entryAdapter = mock(EntryAdapter.class);
+        when(entryAdapter.isAmbient()).thenReturn(false);
+        when(row.getEntryAdapter()).thenReturn(entryAdapter);
         mStackScroller.addContainerView(row);
 
         // speed bump = 1
@@ -748,6 +752,9 @@
         NotificationEntry entry = mock(NotificationEntry.class);
         when(row.getEntry()).thenReturn(entry);
         when(entry.isAmbient()).thenReturn(true);
+        EntryAdapter entryAdapter = mock(EntryAdapter.class);
+        when(entryAdapter.isAmbient()).thenReturn(true);
+        when(row.getEntryAdapter()).thenReturn(entryAdapter);
         mStackScroller.addContainerView(row);
 
         // speed bump is set to 0
@@ -764,6 +771,9 @@
         NotificationEntry entry = mock(NotificationEntry.class);
         when(row.getEntry()).thenReturn(entry);
         when(entry.isAmbient()).thenReturn(false);
+        EntryAdapter entryAdapter = mock(EntryAdapter.class);
+        when(entryAdapter.isAmbient()).thenReturn(false);
+        when(row.getEntryAdapter()).thenReturn(entryAdapter);
         mStackScroller.addContainerView(row);
 
         // speed bump is 1
@@ -1377,6 +1387,9 @@
         when(row.canViewBeCleared()).thenReturn(true);
         when(row.getEntry()).thenReturn(entry);
         when(entry.isClearable()).thenReturn(true);
+        EntryAdapter entryAdapter = mock(EntryAdapter.class);
+        when(entryAdapter.isClearable()).thenReturn(true);
+        when(row.getEntryAdapter()).thenReturn(entryAdapter);
 
         return row;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 5e95825..8281132 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -135,6 +135,8 @@
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -151,6 +153,7 @@
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProviderTestUtil;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -1229,8 +1232,36 @@
     }
 
     @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
     public void testBubbleSummaryDismissal_suppressesSummaryAndBubbleFromShade() throws Exception {
         // GIVEN a group summary with a bubble child
+        NotificationEntry groupedBubble = mNotificationTestHelper.createBubbleEntryInGroup();
+        GroupEntry groupSummary = mNotificationTestHelper.createGroupEntry(
+                0, List.of(groupedBubble));
+        mEntryListener.onEntryAdded(groupedBubble);
+        when(mCommonNotifCollection.getEntry(groupedBubble.getKey()))
+                .thenReturn(groupedBubble);
+        assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getKey()));
+
+        // WHEN the summary is dismissed
+        mBubblesManager.handleDismissalInterception(groupSummary.getSummary());
+
+        // THEN the summary and bubbled child are suppressed from the shade
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                groupedBubble.getKey(),
+                groupedBubble.getSbn().getGroupKey()));
+        assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+                groupedBubble.getKey(),
+                groupedBubble.getSbn().getGroupKey()));
+        assertTrue(mBubbleData.isSummarySuppressed(
+                groupSummary.getSummary().getSbn().getGroupKey()));
+    }
+
+    @Test
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
+    public void testBubbleSummaryDismissal_suppressesSummaryAndBubbleFromShade_rows()
+            throws Exception {
+        // GIVEN a group summary with a bubble child
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
         mEntryListener.onEntryAdded(groupedBubble.getEntry());
@@ -1253,8 +1284,33 @@
     }
 
     @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
     public void testAppRemovesSummary_removesAllBubbleChildren() throws Exception {
         // GIVEN a group summary with a bubble child
+        NotificationEntry groupedBubble = mNotificationTestHelper.createBubbleEntryInGroup();
+        GroupEntry groupSummary = mNotificationTestHelper.createGroupEntry(
+                0, List.of(groupedBubble));
+        mEntryListener.onEntryAdded(groupedBubble);
+        when(mCommonNotifCollection.getEntry(groupedBubble.getKey()))
+                .thenReturn(groupedBubble);
+        assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getKey()));
+
+        // GIVEN the summary is dismissed
+        mBubblesManager.handleDismissalInterception(groupSummary.getSummary());
+
+        // WHEN the summary is cancelled by the app
+        mEntryListener.onEntryRemoved(groupSummary.getSummary(), REASON_APP_CANCEL);
+
+        // THEN the summary and its children are removed from bubble data
+        assertFalse(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getKey()));
+        assertFalse(mBubbleData.isSummarySuppressed(
+                groupSummary.getSummary().getSbn().getGroupKey()));
+    }
+
+    @Test
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
+    public void testAppRemovesSummary_removesAllBubbleChildren_rows() throws Exception {
+        // GIVEN a group summary with a bubble child
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
         mEntryListener.onEntryAdded(groupedBubble.getEntry());
@@ -1276,9 +1332,52 @@
     }
 
     @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
     public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren()
             throws Exception {
         // GIVEN a group summary with two (non-bubble) children and one bubble child
+        NotificationEntry groupedBubble = mNotificationTestHelper.createBubbleEntryInGroup();
+        GroupEntry groupSummary = mNotificationTestHelper.createGroupEntry(
+                2, List.of(groupedBubble));
+        mEntryListener.onEntryAdded(groupedBubble);
+        when(mCommonNotifCollection.getEntry(groupedBubble.getKey()))
+                .thenReturn(groupedBubble);
+
+        // WHEN the summary is dismissed
+        mBubblesManager.handleDismissalInterception(groupSummary.getSummary());
+
+        // THEN only the NON-bubble children are dismissed
+        List<NotificationEntry> children = groupSummary.getChildren();
+        verify(mNotifCallback, times(1)).removeNotification(
+                eq(children.get(0)), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
+        verify(mNotifCallback, times(1)).removeNotification(
+                eq(children.get(1)), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
+        verify(mNotifCallback, never()).removeNotification(eq(groupedBubble),
+                any(), anyInt());
+
+        // THEN the bubble child still exists as a bubble and is suppressed from the shade
+        assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getKey()));
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                groupedBubble.getKey(),
+                groupedBubble.getSbn().getGroupKey()));
+        assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+                groupedBubble.getKey(),
+                groupedBubble.getSbn().getGroupKey()));
+
+        // THEN the summary is also suppressed from the shade
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                groupSummary.getSummary().getKey(),
+                groupSummary.getSummary().getSbn().getGroupKey()));
+        assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+                groupSummary.getSummary().getKey(),
+                groupSummary.getSummary().getSbn().getGroupKey()));
+    }
+
+    @Test
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
+    public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren_row()
+            throws Exception {
+        // GIVEN a group summary with two (non-bubble) children and one bubble child
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
         mEntryListener.onEntryAdded(groupedBubble.getEntry());
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
index 43b57de..d6b625b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.biometrics.ui.viewmodel
 
 import android.content.applicationContext
+import android.view.accessibility.accessibilityManager
 import com.android.app.activityTaskManager
 import com.android.launcher3.icons.IconProvider
 import com.android.systemui.biometrics.domain.interactor.biometricStatusInteractor
@@ -27,7 +28,6 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.util.mockito.mock
-import org.mockito.Mockito.mock
 
 val Kosmos.promptViewModel by Fixture {
     PromptViewModel(
@@ -39,6 +39,7 @@
         udfpsUtils = udfpsUtils,
         iconProvider = iconProvider,
         activityTaskManager = activityTaskManager,
+        accessibilityManager = accessibilityManager,
     )
 }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
index a64fc24..d6f0e06 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
@@ -104,6 +104,8 @@
     private val _displayChangeEvent = MutableSharedFlow<Int>(replay = 1)
     override val displayChangeEvent: Flow<Int> = _displayChangeEvent
 
+    override val displayIdsWithSystemDecorations: StateFlow<Set<Int>> = MutableStateFlow(emptySet())
+
     suspend fun emitDisplayChangeEvent(displayId: Int) = _displayChangeEvent.emit(displayId)
 
     fun setDefaultDisplayOff(defaultDisplayOff: Boolean) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisibilityLocationProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisibilityLocationProviderKosmos.kt
new file mode 100644
index 0000000..085d386
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisibilityLocationProviderKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * 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 org.mockito.kotlin.mock
+
+val Kosmos.visibilityLocationProvider: VisibilityLocationProvider by Kosmos.Fixture { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt
index 358d251..1a5c61a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt
@@ -16,7 +16,41 @@
 
 package com.android.systemui.statusbar.notification.collection.coordinator
 
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.wakefulnessLifecycle
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.shade.domain.interactor.shadeAnimationInteractor
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider
+import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
+import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
+import com.android.systemui.statusbar.notification.visibilityLocationProvider
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.util.kotlin.JavaAdapter
 
-var Kosmos.visualStabilityCoordinator by Kosmos.Fixture { mock<VisualStabilityCoordinator>() }
+var Kosmos.visualStabilityCoordinator: VisualStabilityCoordinator by
+    Kosmos.Fixture {
+        VisualStabilityCoordinator(
+            fakeExecutor,
+            fakeExecutor,
+            dumpManager,
+            headsUpNotificationRepository,
+            shadeAnimationInteractor,
+            JavaAdapter(testScope.backgroundScope),
+            seenNotificationsInteractor,
+            statusBarStateController,
+            visibilityLocationProvider,
+            visualStabilityProvider,
+            wakefulnessLifecycle,
+            communalSceneInteractor,
+            shadeInteractor,
+            keyguardTransitionInteractor,
+            keyguardStateController,
+            visualStabilityCoordinatorLogger,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorLoggerKosmos.kt
new file mode 100644
index 0000000..6645cdc
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorLoggerKosmos.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.collection.coordinator
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.visualStabilityCoordinatorLogger: VisualStabilityCoordinatorLogger by
+    Kosmos.Fixture { mock() }
diff --git a/proto/src/metrics_constants/OWNERS b/proto/src/metrics_constants/OWNERS
index 7009282..b032841 100644
--- a/proto/src/metrics_constants/OWNERS
+++ b/proto/src/metrics_constants/OWNERS
@@ -1,4 +1,3 @@
 cwren@android.com
-yanglu@google.com
 yaochen@google.com
 yro@google.com
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java
index db3cd8ed..1153a77 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java
@@ -35,14 +35,4 @@
 @Retention(SOURCE)
 @Target({FIELD, METHOD, PARAMETER, TYPE_USE})
 @libcore.api.IntraCoreApi
-public @interface NonNull {
-   /**
-    * Min Android API level (inclusive) to which this annotation is applied.
-    */
-   int from() default Integer.MIN_VALUE;
-
-   /**
-    * Max Android API level to which this annotation is applied.
-    */
-   int to() default Integer.MAX_VALUE;
-}
+public @interface NonNull {}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java
index 3371978..295f083 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java
@@ -35,14 +35,4 @@
 @Retention(SOURCE)
 @Target({FIELD, METHOD, PARAMETER, TYPE_USE})
 @libcore.api.IntraCoreApi
-public @interface Nullable {
-   /**
-    * Min Android API level (inclusive) to which this annotation is applied.
-    */
-   int from() default Integer.MIN_VALUE;
-
-   /**
-    * Max Android API level to which this annotation is applied.
-    */
-   int to() default Integer.MAX_VALUE;
-}
+public @interface Nullable {}
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
index c5a2f9f..bba4681 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
@@ -15,6 +15,7 @@
  */
 package com.android.hoststubgen.filters
 
+import com.android.hoststubgen.log
 import org.objectweb.asm.commons.Remapper
 
 /**
@@ -23,19 +24,25 @@
 class FilterRemapper(val filter: OutputFilter) : Remapper() {
     private val cache = mutableMapOf<String, String>()
 
-    override fun mapType(typeInternalName: String?): String? {
+
+    override fun map(typeInternalName: String?): String? {
         if (typeInternalName == null) {
             return null
         }
 
         cache[typeInternalName]?.let {
+            // log.d("Cached rename from $typeInternalName to $it")
             return it
         }
 
-        var mapped = filter.remapType(typeInternalName) ?: typeInternalName
+        var mapped = filter.remapType(typeInternalName)
+        if (mapped != null) {
+            log.d("Renaming type $typeInternalName to $mapped")
+        } else {
+            // log.d("Not renaming type $typeInternalName")
+        }
+        mapped = mapped ?: typeInternalName
         cache[typeInternalName] = mapped
         return mapped
     }
-
-    // TODO Do we need to implement mapPackage(), etc too?
 }
\ No newline at end of file
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
index a78c655..bc90d12 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
@@ -15,7 +15,6 @@
  */
 package com.android.hoststubgen.filters
 
-import com.android.hoststubgen.log
 import java.util.regex.Pattern
 
 /**
@@ -34,17 +33,12 @@
         val typeInternalNamePrefix: String,
     )
 
-    private val cache = mutableMapOf<String, String>()
-
     override fun remapType(className: String): String? {
-        var mapped: String = className
         typeRenameSpecs.forEach {
             if (it.typeInternalNamePattern.matcher(className).matches()) {
-                mapped = it.typeInternalNamePrefix + className
-                log.d("Renaming type $className to $mapped")
+                return it.typeInternalNamePrefix + className
             }
         }
-        cache[className] = mapped
-        return mapped
+        return null
     }
 }
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 635f66d..e846d6e 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -3198,7 +3198,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 2
+  interfaces: 0, fields: 0, methods: 3, attributes: 2
 Constant pool:
 {
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
@@ -3229,6 +3229,22 @@
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0      12     0 value   I
+
+  public static int bar(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: iload_0
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getArray:(I)[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+         x: iconst_0
+         x: aaload
+         x: invokevirtual #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0 value   I
 }
 SourceFile: "TinyFrameworkRenamedClassCaller.java"
 RuntimeInvisibleAnnotations:
@@ -3242,7 +3258,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 2
+  interfaces: 0, fields: 1, methods: 3, attributes: 2
 Constant pool:
 {
   private final int mValue;
@@ -3278,6 +3294,26 @@
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed[] getArray(int);
+    descriptor: (I)[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=6, locals=1, args_size=1
+         x: iconst_1
+         x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: dup
+         x: iconst_0
+         x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: dup
+        x: iload_0
+        x: invokespecial #x                 // Method "<init>":(I)V
+        x: aastore
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      16     0 value   I
 }
 SourceFile: "TinyFrameworkToBeRenamed.java"
 RuntimeInvisibleAnnotations:
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt
index 51a3355..be95fe0 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt
@@ -3392,7 +3392,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 3
+  interfaces: 0, fields: 0, methods: 3, attributes: 3
 Constant pool:
 {
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
@@ -3429,6 +3429,25 @@
     RuntimeVisibleAnnotations:
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int bar(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: iload_0
+         x: invokestatic  #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getArray:(I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+         x: iconst_0
+         x: aaload
+         x: invokevirtual #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 }
 SourceFile: "TinyFrameworkRenamedClassCaller.java"
 RuntimeVisibleAnnotations:
@@ -3867,7 +3886,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 3
+  interfaces: 0, fields: 1, methods: 3, attributes: 3
 Constant pool:
 {
   private final int mValue;
@@ -3891,7 +3910,7 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+            0      10     0  this   Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
             0      10     1 value   I
     RuntimeVisibleAnnotations:
       x: #x()
@@ -3908,7 +3927,30 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+            0       5     0  this   Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed[] getArray(int);
+    descriptor: (I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=6, locals=1, args_size=1
+         x: iconst_1
+         x: anewarray     #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: dup
+         x: iconst_0
+         x: new           #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: dup
+        x: iload_0
+        x: invokespecial #x                 // Method "<init>":(I)V
+        x: aastore
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      16     0 value   I
     RuntimeVisibleAnnotations:
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
index a466a2e..667981f 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
@@ -4241,7 +4241,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 3
+  interfaces: 0, fields: 0, methods: 4, attributes: 3
 Constant pool:
 {
   private static {};
@@ -4298,6 +4298,30 @@
     RuntimeVisibleAnnotations:
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int bar(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+         x: ldc           #x                 // String bar
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_0
+        x: invokestatic  #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getArray:(I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+        x: iconst_0
+        x: aaload
+        x: invokevirtual #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 }
 SourceFile: "TinyFrameworkRenamedClassCaller.java"
 RuntimeVisibleAnnotations:
@@ -4947,7 +4971,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 3, attributes: 3
+  interfaces: 0, fields: 1, methods: 4, attributes: 3
 Constant pool:
 {
   private final int mValue;
@@ -4962,8 +4986,8 @@
     flags: (0x000a) ACC_PRIVATE, ACC_STATIC
     Code:
       stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: ldc           #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
          x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
          x: return
 
@@ -4972,7 +4996,7 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: ldc           #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
          x: ldc           #x                 // String <init>
          x: ldc           #x                 // String (I)V
          x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -4986,7 +5010,7 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+           11      10     0  this   Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
            11      10     1 value   I
     RuntimeVisibleAnnotations:
       x: #x()
@@ -4997,7 +5021,7 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: ldc           #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
          x: ldc           #x                 // String getValue
          x: ldc           #x                 // String ()I
          x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -5008,7 +5032,35 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+           11       5     0  this   Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed[] getArray(int);
+    descriptor: (I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=6, locals=1, args_size=1
+         x: ldc           #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: ldc           #x                 // String getArray
+         x: ldc           #x                 // String (I)[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_1
+        x: anewarray     #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+        x: dup
+        x: iconst_0
+        x: new           #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+        x: dup
+        x: iload_0
+        x: invokespecial #x                 // Method "<init>":(I)V
+        x: aastore
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      16     0 value   I
     RuntimeVisibleAnnotations:
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 78341d7..8f56f0e 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -3219,7 +3219,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 2
+  interfaces: 0, fields: 0, methods: 3, attributes: 2
 Constant pool:
 {
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
@@ -3250,6 +3250,22 @@
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0      12     0 value   I
+
+  public static int bar(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: iload_0
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getArray:(I)[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+         x: iconst_0
+         x: aaload
+         x: invokevirtual #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0 value   I
 }
 SourceFile: "TinyFrameworkRenamedClassCaller.java"
 RuntimeInvisibleAnnotations:
@@ -3263,7 +3279,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 2
+  interfaces: 0, fields: 1, methods: 3, attributes: 2
 Constant pool:
 {
   private final int mValue;
@@ -3299,6 +3315,26 @@
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed[] getArray(int);
+    descriptor: (I)[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=6, locals=1, args_size=1
+         x: iconst_1
+         x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: dup
+         x: iconst_0
+         x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: dup
+        x: iload_0
+        x: invokespecial #x                 // Method "<init>":(I)V
+        x: aastore
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      16     0 value   I
 }
 SourceFile: "TinyFrameworkToBeRenamed.java"
 RuntimeInvisibleAnnotations:
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
index 2e0b182..c918bf8 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
@@ -3422,7 +3422,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 3
+  interfaces: 0, fields: 0, methods: 3, attributes: 3
 Constant pool:
 {
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
@@ -3459,6 +3459,25 @@
     RuntimeVisibleAnnotations:
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int bar(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: iload_0
+         x: invokestatic  #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getArray:(I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+         x: iconst_0
+         x: aaload
+         x: invokevirtual #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 }
 SourceFile: "TinyFrameworkRenamedClassCaller.java"
 RuntimeVisibleAnnotations:
@@ -3897,7 +3916,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 3
+  interfaces: 0, fields: 1, methods: 3, attributes: 3
 Constant pool:
 {
   private final int mValue;
@@ -3921,7 +3940,7 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+            0      10     0  this   Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
             0      10     1 value   I
     RuntimeVisibleAnnotations:
       x: #x()
@@ -3938,7 +3957,30 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+            0       5     0  this   Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed[] getArray(int);
+    descriptor: (I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=6, locals=1, args_size=1
+         x: iconst_1
+         x: anewarray     #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: dup
+         x: iconst_0
+         x: new           #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: dup
+        x: iload_0
+        x: invokespecial #x                 // Method "<init>":(I)V
+        x: aastore
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      16     0 value   I
     RuntimeVisibleAnnotations:
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
index 51f7925..28065bf 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
@@ -4271,7 +4271,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 3
+  interfaces: 0, fields: 0, methods: 4, attributes: 3
 Constant pool:
 {
   private static {};
@@ -4328,6 +4328,30 @@
     RuntimeVisibleAnnotations:
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int bar(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+         x: ldc           #x                 // String bar
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_0
+        x: invokestatic  #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getArray:(I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+        x: iconst_0
+        x: aaload
+        x: invokevirtual #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 }
 SourceFile: "TinyFrameworkRenamedClassCaller.java"
 RuntimeVisibleAnnotations:
@@ -4977,7 +5001,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 3, attributes: 3
+  interfaces: 0, fields: 1, methods: 4, attributes: 3
 Constant pool:
 {
   private final int mValue;
@@ -4992,8 +5016,8 @@
     flags: (0x000a) ACC_PRIVATE, ACC_STATIC
     Code:
       stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: ldc           #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
          x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
          x: return
 
@@ -5002,7 +5026,7 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: ldc           #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
          x: ldc           #x                 // String <init>
          x: ldc           #x                 // String (I)V
          x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -5016,7 +5040,7 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+           11      10     0  this   Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
            11      10     1 value   I
     RuntimeVisibleAnnotations:
       x: #x()
@@ -5027,7 +5051,7 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: ldc           #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
          x: ldc           #x                 // String getValue
          x: ldc           #x                 // String ()I
          x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -5038,7 +5062,35 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+           11       5     0  this   Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed[] getArray(int);
+    descriptor: (I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=6, locals=1, args_size=1
+         x: ldc           #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: ldc           #x                 // String getArray
+         x: ldc           #x                 // String (I)[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_1
+        x: anewarray     #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+        x: dup
+        x: iconst_0
+        x: new           #x                  // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+        x: dup
+        x: iload_0
+        x: invokespecial #x                 // Method "<init>":(I)V
+        x: aastore
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      16     0 value   I
     RuntimeVisibleAnnotations:
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
index 707bc0e..74e4610 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
@@ -25,4 +25,9 @@
         // so this code should work as-is.
         return new TinyFrameworkToBeRenamed(value).getValue();
     }
+
+    /** Calls the class that'll be renamed. */
+    public static int bar(int value) {
+        return TinyFrameworkToBeRenamed.getArray(value)[0].getValue();
+    }
 }
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
index 8319ced..7dcc83e 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
@@ -31,4 +31,8 @@
     public int getValue() {
         return mValue;
     }
+
+    public static TinyFrameworkToBeRenamed[] getArray(int value) {
+        return new TinyFrameworkToBeRenamed[] { new TinyFrameworkToBeRenamed(value) };
+    }
 }
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 68673dc..b8d6be4 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -308,6 +308,11 @@
     }
 
     @Test
+    public void testTypeRenameArray() {
+        assertThat(TinyFrameworkRenamedClassCaller.bar(2)).isEqualTo(2);
+    }
+
+    @Test
     public void testMethodCallReplaceNonStatic() throws Exception {
         assertThat(TinyFrameworkMethodCallReplace.nonStaticMethodCallReplaceTester())
                 .isEqualTo(true);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 1c95184..42834ce 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2137,9 +2137,6 @@
                         this, 0, oldUserState.mUserId));
             }
 
-            // Announce user changes only if more than one exist.
-            final boolean announceNewUser = mUserManager.getUsers().size() > 1;
-
             // The user changed.
             mCurrentUserId = userId;
             AccessibilityUserState userState = getCurrentUserStateLocked();
@@ -2166,13 +2163,6 @@
             // As an initialization step, update the shortcuts for the current user.
             updateShortcutsForCurrentNavigationMode();
 
-            if (announceNewUser) {
-                // Schedule announcement of the current user if needed.
-                mMainHandler.sendMessageDelayed(
-                        obtainMessage(AccessibilityManagerService::announceNewUserIfNeeded, this),
-                        WAIT_FOR_USER_STATE_FULLY_INITIALIZED_MILLIS);
-            }
-
             for (IUserInitializationCompleteCallback callback
                     : mUserInitializationCompleteCallbacks) {
                 try {
@@ -2186,20 +2176,6 @@
         }
     }
 
-    private void announceNewUserIfNeeded() {
-        synchronized (mLock) {
-            AccessibilityUserState userState = getCurrentUserStateLocked();
-            if (userState.isHandlingAccessibilityEventsLocked()) {
-                String message = mContext.getString(R.string.user_switched,
-                        mUserManager.getUserInfo(mCurrentUserId).name);
-                AccessibilityEvent event = AccessibilityEvent.obtain(
-                        AccessibilityEvent.TYPE_ANNOUNCEMENT);
-                event.getText().add(message);
-                sendAccessibilityEventLocked(event, mCurrentUserId);
-            }
-        }
-    }
-
     private void unlockUser(int userId) {
         synchronized (mLock) {
             int parentUserId = mSecurityPolicy.resolveProfileParentLocked(userId);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 125824c..4187ee2 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -387,6 +387,15 @@
     @Overridable
     public static final long FGS_SAW_RESTRICTIONS = 319471980L;
 
+    /**
+     * Allows system to manage foreground state of service with type
+     * <li>{@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK}</li>
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = VERSION_CODES.VANILLA_ICE_CREAM)
+    @Overridable
+    public static final long MEDIA_FGS_STATE_TRANSITION = 281762171L;
+
     final ActivityManagerService mAm;
 
     // Maximum number of services that we allow to start in the background
@@ -9341,20 +9350,32 @@
                         == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE
                         && sr.foregroundId == notificationId) {
                     // check if service is explicitly requested by app to not be in foreground.
-                    if (sr.systemRequestedFgToBg) {
-                        Slog.d(TAG,
-                                "System initiated service transition to foreground "
-                                        + "for package "
-                                        + packageName);
-                        setServiceForegroundInnerLocked(sr, sr.foregroundId,
-                                sr.foregroundNoti, /* flags */ 0,
-                                ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
-                                /* callingUidStart */ 0, /* systemRequestedTransition */ true);
+                    if (sr.systemRequestedFgToBg && CompatChanges.isChangeEnabled(
+                            MEDIA_FGS_STATE_TRANSITION, sr.appInfo.uid)) {
+                        if (DEBUG_FOREGROUND_SERVICE) {
+                            Slog.d(TAG,
+                                    "System initiated service transition to foreground "
+                                            + "for package "
+                                            + packageName);
+                        }
+                        try {
+                            setServiceForegroundInnerLocked(sr, sr.foregroundId,
+                                    sr.foregroundNoti, /* flags */ 0,
+                                    ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
+                                    /* callingUidStart */ 0, /* systemRequestedTransition */ true);
+                        } catch (Exception e) {
+                            Slog.w(TAG,
+                                    "Exception in system initiated foreground service transition "
+                                            + "for package " + packageName
+                                            + ":" + e.toString());
+                        }
                     } else {
-                        Slog.d(TAG,
-                                "Ignoring system initiated foreground service transition for "
-                                        + "package"
-                                        + packageName);
+                        if (DEBUG_FOREGROUND_SERVICE) {
+                            Slog.d(TAG,
+                                    "Ignoring system initiated foreground service transition for "
+                                            + "package "
+                                            + packageName);
+                        }
                     }
                 }
             }
@@ -9388,14 +9409,32 @@
                 if (sr.foregroundServiceType
                         == ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
                         && sr.foregroundId == notificationId) {
-                    Slog.d(TAG,
-                            "System initiated transition of foreground service(type:media) to bg "
-                                    + "for package"
-                                    + packageName);
-                    setServiceForegroundInnerLocked(sr, /* id */ 0,
-                            /* notification */ null, /* flags */ 0,
-                            /* foregroundServiceType */ 0, /* callingUidStart */ 0,
-                            /* systemRequestedTransition */ true);
+                    if (CompatChanges.isChangeEnabled(MEDIA_FGS_STATE_TRANSITION, sr.appInfo.uid)) {
+                        if (DEBUG_FOREGROUND_SERVICE) {
+                            Slog.d(TAG,
+                                    "System initiated transition of foreground service"
+                                            + "(type:media) to"
+                                            + " bg "
+                                            + "for package "
+                                            + packageName);
+                        }
+                        try {
+                            setServiceForegroundInnerLocked(sr, /* id */ 0,
+                                    /* notification */ null, /* flags */ 0,
+                                    /* foregroundServiceType */ 0, /* callingUidStart */ 0,
+                                    /* systemRequestedTransition */ true);
+                        } catch (Exception e) {
+                            Slog.wtf(TAG,
+                                    "Exception in system initiated background service transition "
+                                            + "for package " + packageName
+                                            + ":" + e.toString());
+                        }
+                    } else {
+                        if (DEBUG_FOREGROUND_SERVICE) {
+                            Slog.d(TAG, "Ignoring system initiated transition of foreground"
+                                    + " service(type:media)to bg for package " + packageName);
+                        }
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index f1007e7..0954c49 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -238,6 +238,8 @@
         "pixel_connectivity_gps",
         "pixel_continuity",
         "pixel_display",
+        "pixel_fingerprint",
+        "pixel_gsc",
         "pixel_perf",
         "pixel_sensai",
         "pixel_sensors",
@@ -245,6 +247,7 @@
         "pixel_system_sw_video",
         "pixel_video_sw",
         "pixel_watch",
+        "pixel_wifi",
         "platform_compat",
         "platform_security",
         "pixel_watch_debug_trace",
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 964b97c..551202c 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -1051,7 +1051,9 @@
 
                     void handleHdrSdrNitsChanged(float displayNits, float sdrNits) {
                         final float newHdrSdrRatio;
-                        if (displayNits != INVALID_NITS && sdrNits != INVALID_NITS) {
+                        if (displayNits != INVALID_NITS && sdrNits != INVALID_NITS
+                            && (mBacklightAdapter.mUseSurfaceControlBrightness ||
+                                mBacklightAdapter.mForceSurfaceControl)) {
                             // Ensure the ratio stays >= 1.0f as values below that are nonsensical
                             newHdrSdrRatio = Math.max(1.f, displayNits / sdrNits);
                         } else {
diff --git a/services/core/java/com/android/server/display/color/OWNERS b/services/core/java/com/android/server/display/color/OWNERS
index 27adf12..8f5a9a0 100644
--- a/services/core/java/com/android/server/display/color/OWNERS
+++ b/services/core/java/com/android/server/display/color/OWNERS
@@ -1,4 +1,3 @@
 christyfranks@google.com
-justinklaassen@google.com
 
-per-file DisplayTransformManager.java=michaelwr@google.com
\ No newline at end of file
+per-file DisplayTransformManager.java=michaelwr@google.com
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 7e8bb28..144caea 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -1365,6 +1365,21 @@
             }
         }
 
+        @Override
+        public void setScreensaverEnabled(boolean enabled) {
+            checkPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
+            final UserHandle userHandle = getCallingUserHandle();
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                        Settings.Secure.SCREENSAVER_ENABLED, enabled ? 1 : 0,
+                        userHandle.getIdentifier());
+                mPowerManagerInternal.updateSettings();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
         boolean canLaunchDreamActivity(String dreamPackageName, String packageName,
                 int callingUid) {
             if (dreamPackageName == null || packageName == null) {
diff --git a/services/core/java/com/android/server/input/InputDataStore.java b/services/core/java/com/android/server/input/InputDataStore.java
index 834f815..6ed50b6 100644
--- a/services/core/java/com/android/server/input/InputDataStore.java
+++ b/services/core/java/com/android/server/input/InputDataStore.java
@@ -89,6 +89,10 @@
             final InputStream inputStream = mInputGestureFileInjector.openRead(userId);
             inputGestureDataList = readInputGesturesXml(inputStream, false);
             inputStream.close();
+        } catch (FileNotFoundException exception) {
+            // There are valid reasons for the file to be missing, such as shortcuts having not
+            // been registered by the user.
+            return List.of();
         } catch (IOException exception) {
             // In case we are unable to read from the file on disk or another IO operation error,
             // fail gracefully.
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 4f3aa06..280f3b7 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -120,12 +120,21 @@
      * Binding flags for establishing connection to the {@link InputMethodService}.
      */
     @VisibleForTesting
-    static final int IME_CONNECTION_BIND_FLAGS =
-            Context.BIND_AUTO_CREATE
+    static final int IME_CONNECTION_BIND_FLAGS;
+    static {
+        if (android.view.inputmethod.Flags.lowerImeOomImportance()) {
+            IME_CONNECTION_BIND_FLAGS = Context.BIND_AUTO_CREATE
+                    | Context.BIND_ALMOST_PERCEPTIBLE
+                    | Context.BIND_IMPORTANT_BACKGROUND
+                    | Context.BIND_SCHEDULE_LIKE_TOP_APP;
+        } else {
+            IME_CONNECTION_BIND_FLAGS = Context.BIND_AUTO_CREATE
                     | Context.BIND_NOT_VISIBLE
                     | Context.BIND_NOT_FOREGROUND
                     | Context.BIND_IMPORTANT_BACKGROUND
                     | Context.BIND_SCHEDULE_LIKE_TOP_APP;
+        }
+    }
 
     private final int mImeConnectionBindFlags;
 
diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS
index e507c6b..9d8aef9 100644
--- a/services/core/java/com/android/server/inputmethod/OWNERS
+++ b/services/core/java/com/android/server/inputmethod/OWNERS
@@ -1,7 +1,6 @@
 set noparent
 
 roosa@google.com
-yukawa@google.com
 tarandeep@google.com
 fstern@google.com
 cosminbaies@google.com
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
index 463989a..60d028b 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -390,10 +390,11 @@
                 .max(Comparator.comparingLong(PackageInstaller.SessionInfo::getCreatedMillis));
     }
 
-    // ADB sets installerPackageName to null, this creates a loophole to bypass BIC which will be
-    // addressed with b/265203007
     private boolean installedByAdb(String initiatingPackageName) {
-        if(PackageManagerServiceUtils.isInstalledByAdb(initiatingPackageName)) {
+        // GTS tests needs to adopt shell identity to install apps.
+        if(!SystemProperties.get("gts.transparency.bg-install-apps").isEmpty()) {
+            Slog.d(TAG, "handlePackageAdd: is GTS tests, skipping ADB check");
+        } else if(PackageManagerServiceUtils.isInstalledByAdb(initiatingPackageName)) {
             Slog.d(TAG, "handlePackageAdd: is installed by ADB, skipping");
             return true;
         }
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 62b89f32..f98ec04 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -1,7 +1,6 @@
 hackbod@android.com
 hackbod@google.com
 jsharkey@android.com
-jsharkey@google.com
 narayan@google.com
 include /PACKAGE_MANAGER_OWNERS
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 136cb12..635ef06 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -5189,7 +5189,9 @@
                     "Session " + sessionId + " is a parent of multi-package session and "
                             + "requestUserPreapproval on the parent session isn't supported.");
         }
-
+        if (statusReceiver == null) {
+            throw new IllegalArgumentException("Status receiver cannot be null.");
+        }
         synchronized (mLock) {
             assertPreparedAndNotSealedLocked("request of session " + sessionId);
             mPreapprovalDetails = details;
@@ -5542,6 +5544,10 @@
      */
     private static void sendOnUserActionRequired(Context context, IntentSender target,
             int sessionId, Intent intent) {
+        if (target == null) {
+            Slog.e(TAG, "Missing receiver for pending user action.");
+            return;
+        }
         final Intent fillIn = new Intent();
         fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
         fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION);
diff --git a/services/core/java/com/android/server/pm/dex/OWNERS b/services/core/java/com/android/server/pm/dex/OWNERS
index 5ca8ddd..70af4e7 100644
--- a/services/core/java/com/android/server/pm/dex/OWNERS
+++ b/services/core/java/com/android/server/pm/dex/OWNERS
@@ -1,4 +1,3 @@
-alanstokes@google.com
 jiakaiz@google.com
 ngeoffray@google.com
 mast@google.com
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index e3eced2..dd454cd 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -7445,6 +7445,13 @@
         public void setDevicePostured(boolean isPostured) {
             setDevicePosturedInternal(isPostured);
         }
+
+        @Override
+        public void updateSettings() {
+            synchronized (mLock) {
+                updateSettingsLocked();
+            }
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java b/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java
index 29cc9ea..5563f98 100644
--- a/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java
+++ b/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java
@@ -251,6 +251,10 @@
         try (FileInputStream stream = file.openRead()) {
             byte[] header = new byte[FILE_FORMAT_BYTES];
             if (stream.read(header, 0, FILE_FORMAT_BYTES) == -1) {
+                if (file.getBaseFile().length() == 0) {
+                    return new byte[0];
+                }
+
                 Slog.e(TAG, "Invalid battery history file format " + file.getBaseFile());
                 deleteFragment(fragment);
                 return null;
diff --git a/services/core/java/com/android/server/uri/OWNERS b/services/core/java/com/android/server/uri/OWNERS
index cdc07ed..6599db7 100644
--- a/services/core/java/com/android/server/uri/OWNERS
+++ b/services/core/java/com/android/server/uri/OWNERS
@@ -1,3 +1,2 @@
 jsharkey@android.com
-jsharkey@google.com
 varunshah@google.com
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
index 5eed5470..cb82f48 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
@@ -183,9 +184,10 @@
 
     private boolean shouldEnableCameraCompatFreeformTreatmentForApp() {
         if (mCameraCompatAllowOrientationTreatmentOptProp != null) {
+            // OptProp is not-null iff the opt-out flag is on.
             return mCameraCompatAllowOrientationTreatmentOptProp
                     .shouldEnableWithOptOutOverrideAndProperty(isChangeEnabled(mActivityRecord,
-                            OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT));
+                            OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION));
         } else {
             return isChangeEnabled(mActivityRecord,
                     OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT);
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index d6ae651..c26acec 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -490,7 +490,7 @@
                     return null;
                 }
                 final Task taskToRecord = wc.asTask();
-                if (taskToRecord == null) {
+                if (taskToRecord == null || !taskToRecord.isAttached()) {
                     handleStartRecordingFailed();
                     ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
                             "Content Recording: Unable to retrieve task to start recording for "
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index b6f74a0..a4eeb68 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -16,6 +16,10 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.DesktopModeHelper.canEnterDesktopMode;
@@ -23,10 +27,13 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
+import android.app.WindowConfiguration;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.util.Slog;
+import android.window.DesktopModeFlags;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
 /**
  * The class that defines default launch params for tasks in desktop mode
@@ -74,6 +81,13 @@
             appendLog("task null, skipping");
             return RESULT_SKIP;
         }
+
+        if (DesktopModeFlags.DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX.isTrue()
+                && !isEnteringDesktopMode(task, options, currentParams)) {
+            appendLog("not entering desktop mode, skipping");
+            return RESULT_SKIP;
+        }
+
         if (com.android.window.flags.Flags.fixLayoutExistingTask()
                 && task.getCreatedByOrganizerTask() != null) {
             appendLog("has created-by-organizer-task, skipping");
@@ -122,6 +136,62 @@
         return RESULT_CONTINUE;
     }
 
+    /**
+     * Returns true if a task is entering desktop mode, due to its windowing mode being freeform or
+     * if there exists other freeform tasks on the display.
+     */
+    @VisibleForTesting
+    boolean isEnteringDesktopMode(
+            @NonNull Task task,
+            @Nullable ActivityOptions options,
+            @NonNull LaunchParamsController.LaunchParams currentParams) {
+        //  As freeform tasks cannot exist outside of desktop mode, it is safe to assume if
+        //  freeform tasks are visible we are in desktop mode and as a result any launching
+        //  activity will also enter desktop mode. On this same relationship, we can also assume
+        //  if there are not visible freeform tasks but a freeform activity is now launching, it
+        //  will force the device into desktop mode.
+        return (task.getDisplayContent().getTopMostVisibleFreeformActivity() != null
+                    && checkSourceWindowModesCompatible(task, options, currentParams))
+                || isRequestingFreeformWindowMode(task, options, currentParams);
+    }
+
+    private boolean isRequestingFreeformWindowMode(
+            @NonNull Task task,
+            @Nullable ActivityOptions options,
+            @NonNull LaunchParamsController.LaunchParams currentParams) {
+        return task.inFreeformWindowingMode()
+                || (options != null && options.getLaunchWindowingMode() == WINDOWING_MODE_FREEFORM)
+                || (currentParams.hasWindowingMode()
+                && currentParams.mWindowingMode == WINDOWING_MODE_FREEFORM);
+    }
+
+    /**
+     * Returns true is all possible source window modes are compatible with desktop mode.
+     */
+    private boolean checkSourceWindowModesCompatible(
+            @NonNull Task task,
+            @Nullable ActivityOptions options,
+            @NonNull LaunchParamsController.LaunchParams currentParams) {
+        return isCompatibleDesktopWindowingMode(task.getWindowingMode())
+                && (options == null
+                    || isCompatibleDesktopWindowingMode(options.getLaunchWindowingMode()))
+                && isCompatibleDesktopWindowingMode(currentParams.mWindowingMode);
+    }
+
+    /**
+     * Returns true is the requesting window mode is one that can lead to the activity entering
+     * desktop.
+     */
+    private boolean isCompatibleDesktopWindowingMode(
+            @WindowConfiguration.WindowingMode int windowingMode) {
+        return switch (windowingMode) {
+            case WINDOWING_MODE_UNDEFINED,
+                 WINDOWING_MODE_FULLSCREEN,
+                 WINDOWING_MODE_FREEFORM -> true;
+            default -> false;
+        };
+    }
+
     private void initLogBuilder(Task task, ActivityRecord activity) {
         if (DEBUG) {
             mLogBuilder = new StringBuilder(
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 50f12c3..64c19ff 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3674,6 +3674,11 @@
 
         pw.println();
         super.dump(pw, prefix, dumpAll);
+        pw.print(prefix);
+        if (mHasSetIgnoreOrientationRequest) {
+            pw.print("mHasSetIgnoreOrientationRequest=true ");
+        }
+        pw.print("ignoreOrientationRequest="); pw.println(getIgnoreOrientationRequest());
         pw.print(prefix); pw.print("mLayoutSeq="); pw.println(mLayoutSeq);
 
         pw.print("  mCurrentFocus="); pw.println(mCurrentFocus);
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 1a90bcc..9cf792d 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -25,6 +25,7 @@
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
 
 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ORIENTATION;
+import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ORIENTATION_CHANGE;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
 import static com.android.server.wm.DisplayRotationProto.FIXED_TO_USER_ROTATION_MODE;
 import static com.android.server.wm.DisplayRotationProto.FROZEN_TO_USER_ROTATION;
@@ -587,11 +588,6 @@
                 ActivityInfo.screenOrientationToString(lastOrientation), lastOrientation,
                 Surface.rotationToString(oldRotation), oldRotation);
 
-        ProtoLog.v(WM_DEBUG_ORIENTATION,
-                "Display id=%d selected orientation %s (%d), got rotation %s (%d)", displayId,
-                ActivityInfo.screenOrientationToString(lastOrientation), lastOrientation,
-                Surface.rotationToString(rotation), rotation);
-
         if (oldRotation == rotation) {
             // No change.
             return false;
@@ -601,9 +597,11 @@
             mDisplayRotationCoordinator.onDefaultDisplayRotationChanged(rotation);
         }
 
-        ProtoLog.v(WM_DEBUG_ORIENTATION,
-                "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
-                        displayId, rotation, oldRotation, lastOrientation);
+        ProtoLog.i(WM_DEBUG_ORIENTATION_CHANGE, "Display id=%d rotation changed to %d from %d,"
+                        + " lastOrientation=%d userRotationMode=%d userRotation=%d"
+                        + " lastSensorRotation=%d",
+                displayId, rotation, oldRotation, lastOrientation, mUserRotationMode, mUserRotation,
+                mLastSensorRotation);
 
         mRotation = rotation;
 
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index dede767..243a532 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -3,7 +3,6 @@
 ogunwale@google.com
 jjaggi@google.com
 racarr@google.com
-chaviw@google.com
 vishnun@google.com
 akulian@google.com
 roosa@google.com
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d0d2067..1c3510d 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2007,6 +2007,11 @@
         return getActivity(r -> !r.finishing, true /* traverseTopToBottom */);
     }
 
+    ActivityRecord getTopMostVisibleFreeformActivity() {
+        return getActivity(r -> r.isVisibleRequested() && r.inFreeformWindowingMode(),
+                true /* traverseTopToBottom */);
+    }
+
     ActivityRecord getTopActivity(boolean includeFinishing, boolean includeOverlays) {
         // Break down into 4 calls to avoid object creation due to capturing input params.
         if (includeFinishing) {
diff --git a/services/core/jni/stats/OWNERS b/services/core/jni/stats/OWNERS
index 2611e5b..8d87925 100644
--- a/services/core/jni/stats/OWNERS
+++ b/services/core/jni/stats/OWNERS
@@ -1,8 +1,6 @@
 jeffreyhuang@google.com
-jtnguyen@google.com
 muhammadq@google.com
 sharaieko@google.com
 singhtejinder@google.com
 tsaichristine@google.com
 yaochen@google.com
-yro@google.com
diff --git a/services/musicrecognition/OWNERS b/services/musicrecognition/OWNERS
index 037b048..820be00 100644
--- a/services/musicrecognition/OWNERS
+++ b/services/musicrecognition/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 830636
 
 oni@google.com
-volnov@google.com
 
diff --git a/services/serial/OWNERS b/services/serial/OWNERS
new file mode 100644
index 0000000..89ce78e
--- /dev/null
+++ b/services/serial/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/hardware/serial/OWNERS
\ No newline at end of file
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
index 5fe5b23..d6a6853 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
@@ -18,6 +18,9 @@
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="apct-instrumentation" />
 
+    <!-- Needed for reading the app files for the test artifacts -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="install-arg" value="-t" />
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
index d4be7f8..51da511 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
@@ -231,6 +232,7 @@
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
     public void testShouldApplyCameraCompatFreeformTreatment_notEnabledByOverride_returnsFalse() {
         runTestScenario((robot) -> {
             robot.activity().createActivityWithComponentInNewTask();
@@ -240,21 +242,19 @@
     }
 
     @Test
-    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION})
     @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
             FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
-    public void testShouldApplyCameraCompatFreeformTreatment_propertyFalse_returnsFalse() {
+    public void testShouldApplyCameraCompatFreeformTreatment_disablePropertyOn_returnsFalse() {
         runTestScenario((robot) -> {
             robot.activity().createActivityWithComponentInNewTask();
 
-            robot.prop().disable(PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION);
-
             robot.checkShouldApplyFreeformTreatmentForCameraCompat(false);
         });
     }
 
     @Test
-    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+    @EnableCompatChanges(OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT)
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
     public void testShouldApplyCameraCompatFreeformTreatment_optOutFlagNotEnabled_optOutIgnored() {
@@ -262,18 +262,50 @@
             robot.activity().createActivityWithComponentInNewTask();
 
             robot.prop().disable(PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION);
+            robot.activity().createActivityWithComponentInNewTask();
 
             robot.checkShouldApplyFreeformTreatmentForCameraCompat(true);
         });
     }
 
     @Test
-    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
-    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
-    public void testShouldApplyCameraCompatFreeformTreatment_overrideAndFlagEnabled_returnsTrue() {
+    @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
+            FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
+    public void testShouldApplyCameraCompatFreeformTreatment_optedOutViaProperty_returnsFalse() {
         runTestScenario((robot) -> {
             robot.activity().createActivityWithComponentInNewTask();
 
+            robot.prop().disable(PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION);
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldApplyFreeformTreatmentForCameraCompat(false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges(OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT)
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
+    public void testShouldApplyCameraCompatFreeformTreatment_optInAndFlagEnabled_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponentInNewTask();
+
+            robot.checkShouldApplyFreeformTreatmentForCameraCompat(true);
+        });
+    }
+
+
+    @Test
+    @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
+            FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
+    public void testShouldApplyCameraCompatFreeformTreatment_notOptedOut_flagEnabled_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatment(true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponentInNewTask();
+                robot.prop().enable(PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION);
+            });
+
             robot.checkShouldApplyFreeformTreatmentForCameraCompat(true);
         });
     }
@@ -294,6 +326,8 @@
     @EnableCompatChanges({OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA,
             OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA,
             OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
     public void testShouldRecomputeConfigurationForFreeformTreatment() {
         runTestScenario((robot) -> {
             robot.conf().enableCameraCompatSplitScreenAspectRatio(true);
@@ -307,6 +341,24 @@
     }
 
     @Test
+    @EnableCompatChanges({OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA,
+            OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
+            FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
+    public void testShouldRecomputeConfigurationForFreeformTreatmentWithOptOutMechanism() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatSplitScreenAspectRatio(true);
+            robot.conf().enableCameraCompatTreatment(true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponentInNewTask();
+                robot.prop().enable(PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION);
+            });
+
+            robot.checkShouldApplyFreeformTreatmentForCameraCompat(true);
+        });
+    }
+
+    @Test
     @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
     public void shouldOverrideMinAspectRatioForCamera_overrideEnabled_returnsTrue() {
         runTestScenario((robot) -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
index 5607252..50876c7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -25,6 +25,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
 import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_USER;
@@ -42,6 +43,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 import static com.android.server.wm.AppCompatConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
 import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING;
+import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -134,8 +136,10 @@
     }
 
     @Test
-    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
-    public void testIsCameraRunningAndWindowingModeEligible_overrideDisabled_returnsFalse() {
+    @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
+            FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION})
+    public void testIsCameraRunningAndWindowingModeEligible_disabledViaOverride_returnsFalse() {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
@@ -165,6 +169,7 @@
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+    @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testIsCameraRunningAndWindowingModeEligible_optInFreeformCameraRunning_true() {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
@@ -175,6 +180,17 @@
     }
 
     @Test
+    @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
+            FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
+    public void testIsCameraRunningAndWindowingModeEligible_freeformCameraRunning_true() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+        assertTrue(mCameraCompatFreeformPolicy.isCameraRunningAndWindowingModeEligible(mActivity));
+    }
+
+    @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testIsFreeformLetterboxingForCameraAllowed_overrideDisabled_returnsFalse() {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index d305c2f..3a06fff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -49,6 +49,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
 import android.app.ActivityOptions;
 import android.compat.testing.PlatformCompatChangeRule;
@@ -98,7 +100,8 @@
         mResult = new LaunchParamsController.LaunchParams();
         mResult.reset();
 
-        mTarget = new DesktopModeLaunchParamsModifier(mContext);
+        mTarget = spy(new DesktopModeLaunchParamsModifier(mContext));
+        doReturn(true).when(mTarget).isEnteringDesktopMode(any(), any(), any());
     }
 
     @Test
@@ -137,6 +140,81 @@
     }
 
     @Test
+    @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+            Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
+    public void testReturnsSkipIfIsEnteringDesktopModeFalse() {
+        setupDesktopModeLaunchParamsModifier();
+        when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenReturn(false);
+
+        final Task task = new TaskBuilder(mSupervisor).build();
+
+        assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate());
+    }
+
+    @Test
+    @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+            Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
+    public void testReturnsContinueIfVisibleFreeformTaskExists() {
+        setupDesktopModeLaunchParamsModifier();
+        when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenCallRealMethod();
+
+        final DisplayContent dc = spy(createNewDisplay());
+        final Task existingFreeformTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        doReturn(existingFreeformTask.getRootActivity()).when(dc)
+                .getTopMostVisibleFreeformActivity();
+        final Task launchingTask = new TaskBuilder(mSupervisor).build();
+        launchingTask.onDisplayChanged(dc);
+
+        assertEquals(RESULT_CONTINUE,
+                new CalculateRequestBuilder().setTask(launchingTask).calculate());
+    }
+
+    @Test
+    @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+            Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
+    public void testReturnsContinueIfTaskInFreeform() {
+        setupDesktopModeLaunchParamsModifier();
+        when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenCallRealMethod();
+
+        final Task task = new TaskBuilder(mSupervisor).setWindowingMode(WINDOWING_MODE_FREEFORM)
+                .build();
+
+        assertEquals(RESULT_CONTINUE,
+                new CalculateRequestBuilder().setTask(task).calculate());
+    }
+
+    @Test
+    @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+            Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
+    public void testReturnsContinueIfFreeformRequestViaActivityOptions() {
+        setupDesktopModeLaunchParamsModifier();
+        when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenCallRealMethod();
+
+        final Task task = new TaskBuilder(mSupervisor).build();
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        assertEquals(RESULT_CONTINUE,
+                new CalculateRequestBuilder().setTask(task).setOptions(options).calculate());
+    }
+
+    @Test
+    @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+            Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
+    public void testReturnsContinueIfFreeformRequestViaPreviousModifier() {
+        setupDesktopModeLaunchParamsModifier();
+        when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenCallRealMethod();
+
+        final Task task = new TaskBuilder(mSupervisor).build();
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        assertEquals(RESULT_CONTINUE,
+                new CalculateRequestBuilder().setTask(task).setOptions(options).calculate());
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsSkipIfNotBoundsPhase() {
         setupDesktopModeLaunchParamsModifier();
diff --git a/telecomm/java/android/telecom/OWNERS b/telecomm/java/android/telecom/OWNERS
index 6656a01..0854c5d 100644
--- a/telecomm/java/android/telecom/OWNERS
+++ b/telecomm/java/android/telecom/OWNERS
@@ -3,4 +3,3 @@
 rgreenwalt@google.com
 tgunn@google.com
 breadley@google.com
-hallliu@google.com
diff --git a/tests/EnforcePermission/OWNERS b/tests/EnforcePermission/OWNERS
index 39550a3..160849e 100644
--- a/tests/EnforcePermission/OWNERS
+++ b/tests/EnforcePermission/OWNERS
@@ -1,3 +1,2 @@
 # Bug component: 315013
 tweek@google.com
-brufino@google.com
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index f43cf52..43d5b71 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -113,6 +113,7 @@
         "io/ZipArchive.cpp",
         "link/AutoVersioner.cpp",
         "link/FeatureFlagsFilter.cpp",
+        "link/FlaggedXmlVersioner.cpp",
         "link/FlagDisabledResourceRemover.cpp",
         "link/ManifestFixer.cpp",
         "link/NoDefaultResourceRemover.cpp",
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index fb576df..9e2a4c1 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -547,7 +547,8 @@
   });
 
   std::string_view resource_type = parser->element_name();
-  if (auto flag = ParseFlag(xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag"))) {
+  if (auto flag =
+          ParseFlag(xml::FindAttribute(parser, xml::kSchemaAndroid, xml::kAttrFeatureFlag))) {
     if (options_.flag) {
       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
                    << "Resource flag are not allowed both in the path and in the file");
@@ -1529,7 +1530,7 @@
   ResolvePackage(parser, &maybe_key.value());
   maybe_key.value().SetSource(source);
 
-  auto flag = ParseFlag(xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag"));
+  auto flag = ParseFlag(xml::FindAttribute(parser, xml::kSchemaAndroid, xml::kAttrFeatureFlag));
 
   std::unique_ptr<Item> value = ParseXml(parser, 0, kAllowRawString);
   if (!value) {
@@ -1674,7 +1675,7 @@
     const std::string& element_namespace = parser->element_namespace();
     const std::string& element_name = parser->element_name();
     if (element_namespace.empty() && element_name == "item") {
-      auto flag = ParseFlag(xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag"));
+      auto flag = ParseFlag(xml::FindAttribute(parser, xml::kSchemaAndroid, xml::kAttrFeatureFlag));
       std::unique_ptr<Item> item = ParseXml(parser, typeMask, kNoRawString);
       if (!item) {
         diag_->Error(android::DiagMessage(item_source) << "could not parse array item");
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 0a5cb1f..2a79216 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -58,6 +58,7 @@
 #include "java/ProguardRules.h"
 #include "link/FeatureFlagsFilter.h"
 #include "link/FlagDisabledResourceRemover.h"
+#include "link/FlaggedXmlVersioner.h"
 #include "link/Linkers.h"
 #include "link/ManifestFixer.h"
 #include "link/NoDefaultResourceRemover.h"
@@ -503,10 +504,19 @@
   const ConfigDescription& config = file_op->config;
   ResourceEntry* entry = file_op->entry;
 
+  FlaggedXmlVersioner flagged_xml_versioner;
+  auto flag_split_resources = flagged_xml_versioner.Process(context_, doc);
+
+  std::vector<std::unique_ptr<xml::XmlResource>> final_resources;
   XmlCompatVersioner xml_compat_versioner(&rules_);
   const util::Range<ApiVersion> api_range{config.sdkVersion,
                                           FindNextApiVersionForConfig(entry, config)};
-  return xml_compat_versioner.Process(context_, doc, api_range);
+  for (auto& split_res : flag_split_resources) {
+    auto inner_resources = xml_compat_versioner.Process(context_, split_res.get(), api_range);
+    final_resources.insert(final_resources.end(), std::make_move_iterator(inner_resources.begin()),
+                           std::make_move_iterator(inner_resources.end()));
+  }
+  return final_resources;
 }
 
 ResourceFile::Type XmlFileTypeForOutputFormat(OutputFormat format) {
diff --git a/tools/aapt2/link/FeatureFlagsFilter.cpp b/tools/aapt2/link/FeatureFlagsFilter.cpp
index 23f7838..74066a3 100644
--- a/tools/aapt2/link/FeatureFlagsFilter.cpp
+++ b/tools/aapt2/link/FeatureFlagsFilter.cpp
@@ -51,7 +51,7 @@
  private:
   bool ShouldRemove(std::unique_ptr<xml::Node>& node) {
     if (auto* el = NodeCast<Element>(node.get())) {
-      auto* attr = el->FindAttribute(xml::kSchemaAndroid, "featureFlag");
+      auto* attr = el->FindAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag);
       if (attr == nullptr) {
         return false;
       }
@@ -76,7 +76,7 @@
             // Remove if flag==true && attr=="!flag" (negated) OR flag==false && attr=="flag"
             bool remove = *it->second.enabled == negated;
             if (!remove) {
-              el->RemoveAttribute(xml::kSchemaAndroid, "featureFlag");
+              el->RemoveAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag);
             }
             return remove;
           }
diff --git a/tools/aapt2/link/FlaggedResources_test.cpp b/tools/aapt2/link/FlaggedResources_test.cpp
index 7bea96c..dbef776 100644
--- a/tools/aapt2/link/FlaggedResources_test.cpp
+++ b/tools/aapt2/link/FlaggedResources_test.cpp
@@ -163,7 +163,7 @@
   auto loaded_apk = LoadedApk::LoadApkFromPath(apk_path, &noop_diag);
 
   std::string output;
-  DumpXmlTreeToString(loaded_apk.get(), "res/layout-v22/layout1.xml", &output);
+  DumpXmlTreeToString(loaded_apk.get(), "res/layout-v36/layout1.xml", &output);
   ASSERT_FALSE(output.contains("test.package.trueFlag"));
   ASSERT_TRUE(output.contains("FIND_ME"));
   ASSERT_TRUE(output.contains("test.package.readWriteFlag"));
diff --git a/tools/aapt2/link/FlaggedXmlVersioner.cpp b/tools/aapt2/link/FlaggedXmlVersioner.cpp
new file mode 100644
index 0000000..75c6f17
--- /dev/null
+++ b/tools/aapt2/link/FlaggedXmlVersioner.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#include "link/FlaggedXmlVersioner.h"
+
+#include "SdkConstants.h"
+#include "androidfw/Util.h"
+
+using ::aapt::xml::Element;
+using ::aapt::xml::NodeCast;
+
+namespace aapt {
+
+// An xml visitor that goes through the a doc and removes any elements that are behind non-negated
+// flags. It also removes the featureFlag attribute from elements behind negated flags.
+class AllDisabledFlagsVisitor : public xml::Visitor {
+ public:
+  void Visit(xml::Element* node) override {
+    std::erase_if(node->children, [this](const std::unique_ptr<xml::Node>& node) {
+      return FixupOrShouldRemove(node);
+    });
+    VisitChildren(node);
+  }
+
+  bool HadFlags() const {
+    return had_flags_;
+  }
+
+ private:
+  bool FixupOrShouldRemove(const std::unique_ptr<xml::Node>& node) {
+    if (auto* el = NodeCast<Element>(node.get())) {
+      auto* attr = el->FindAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag);
+      if (attr == nullptr) {
+        return false;
+      }
+
+      had_flags_ = true;
+      // This class assumes all flags are disabled so we want to remove any elements behind flags
+      // unless the flag specification is negated. In the negated case we remove the featureFlag
+      // attribute because we have already determined whether we are keeping the element or not.
+      std::string_view flag_name = util::TrimWhitespace(attr->value);
+      if (flag_name.starts_with('!')) {
+        el->RemoveAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag);
+        return false;
+      } else {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  bool had_flags_ = false;
+};
+
+std::vector<std::unique_ptr<xml::XmlResource>> FlaggedXmlVersioner::Process(IAaptContext* context,
+                                                                            xml::XmlResource* doc) {
+  std::vector<std::unique_ptr<xml::XmlResource>> docs;
+  if ((static_cast<ApiVersion>(doc->file.config.sdkVersion) >= SDK_BAKLAVA) ||
+      (static_cast<ApiVersion>(context->GetMinSdkVersion()) >= SDK_BAKLAVA)) {
+    // Support for read/write flags was added in baklava so if the doc will only get used on
+    // baklava or later we can just return the original doc.
+    docs.push_back(doc->Clone());
+  } else {
+    auto preBaklavaVersion = doc->Clone();
+    AllDisabledFlagsVisitor visitor;
+    preBaklavaVersion->root->Accept(&visitor);
+    docs.push_back(std::move(preBaklavaVersion));
+
+    if (visitor.HadFlags()) {
+      auto baklavaVersion = doc->Clone();
+      baklavaVersion->file.config.sdkVersion = SDK_BAKLAVA;
+      docs.push_back(std::move(baklavaVersion));
+    }
+  }
+  return docs;
+}
+
+}  // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/link/FlaggedXmlVersioner.h b/tools/aapt2/link/FlaggedXmlVersioner.h
new file mode 100644
index 0000000..44ed266
--- /dev/null
+++ b/tools/aapt2/link/FlaggedXmlVersioner.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "process/IResourceTableConsumer.h"
+#include "xml/XmlDom.h"
+
+namespace aapt {
+
+// FlaggedXmlVersioner takes an XmlResource and checks if any elements have read write android
+// flags on them. If the doc doesn't refer to any such flags the returned vector only contains
+// the original doc.
+//
+// Read/write flags within xml resources files is only supported in android baklava and later. If
+// the config resource specifies a version that is baklava or later it returns a vector containing
+// the original XmlResource. Otherwise FlaggedXmlVersioner creates a version of the doc where all
+// flags are assumed disabled and the config version is the same as the original doc, if specified.
+// It also creates an XmlResource where the contents are the same as the original doc and the config
+// version is baklava. The returned vector is composed of these two new docs.
+class FlaggedXmlVersioner {
+ public:
+  std::vector<std::unique_ptr<xml::XmlResource>> Process(IAaptContext* context,
+                                                         xml::XmlResource* doc);
+};
+}  // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/link/FlaggedXmlVersioner_test.cpp b/tools/aapt2/link/FlaggedXmlVersioner_test.cpp
new file mode 100644
index 0000000..0c1314f
--- /dev/null
+++ b/tools/aapt2/link/FlaggedXmlVersioner_test.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+#include "link/FlaggedXmlVersioner.h"
+
+#include "Debug.h"
+#include "SdkConstants.h"
+#include "io/StringStream.h"
+#include "test/Test.h"
+
+using ::aapt::test::ValueEq;
+using ::testing::Eq;
+using ::testing::IsNull;
+using ::testing::NotNull;
+using ::testing::Pointee;
+using ::testing::SizeIs;
+
+namespace aapt {
+
+class FlaggedXmlVersionerTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    context_ = test::ContextBuilder()
+                   .SetCompilationPackage("com.app")
+                   .SetPackageId(0x7f)
+                   .SetPackageType(PackageType::kApp)
+                   .Build();
+  }
+
+ protected:
+  std::unique_ptr<IAaptContext> context_;
+};
+
+static void PrintDocToString(xml::XmlResource* doc, std::string* out) {
+  io::StringOutputStream stream(out, 1024u);
+  text::Printer printer(&stream);
+  Debug::DumpXml(*doc, &printer);
+  stream.Flush();
+}
+
+TEST_F(FlaggedXmlVersionerTest, NoFlagReturnsOriginal) {
+  auto doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+        <TextView />
+        <TextView />
+        <TextView />
+      </LinearLayout>)");
+  doc->file.config.sdkVersion = SDK_GINGERBREAD;
+
+  FlaggedXmlVersioner versioner;
+  auto results = versioner.Process(context_.get(), doc.get());
+  EXPECT_THAT(results.size(), Eq(1));
+  EXPECT_THAT(results[0]->file.config.sdkVersion, Eq(SDK_GINGERBREAD));
+
+  std::string expected;
+  PrintDocToString(doc.get(), &expected);
+  std::string actual;
+  PrintDocToString(results[0].get(), &actual);
+
+  EXPECT_THAT(actual, Eq(expected));
+}
+
+TEST_F(FlaggedXmlVersionerTest, AlreadyBaklavaReturnsOriginal) {
+  auto doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+        <TextView android:featureFlag="package.flag" />
+        <TextView />
+        <TextView />
+      </LinearLayout>)");
+  doc->file.config.sdkVersion = SDK_BAKLAVA;
+
+  FlaggedXmlVersioner versioner;
+  auto results = versioner.Process(context_.get(), doc.get());
+  EXPECT_THAT(results.size(), Eq(1));
+  EXPECT_THAT(results[0]->file.config.sdkVersion, Eq(SDK_BAKLAVA));
+
+  std::string expected;
+  PrintDocToString(doc.get(), &expected);
+  std::string actual;
+  PrintDocToString(results[0].get(), &actual);
+
+  EXPECT_THAT(actual, Eq(expected));
+}
+
+TEST_F(FlaggedXmlVersionerTest, PreBaklavaGetsSplit) {
+  auto doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+        <TextView android:featureFlag="package.flag" /><TextView /><TextView />
+      </LinearLayout>)");
+  doc->file.config.sdkVersion = SDK_GINGERBREAD;
+
+  FlaggedXmlVersioner versioner;
+  auto results = versioner.Process(context_.get(), doc.get());
+  EXPECT_THAT(results.size(), Eq(2));
+  EXPECT_THAT(results[0]->file.config.sdkVersion, Eq(SDK_GINGERBREAD));
+  EXPECT_THAT(results[1]->file.config.sdkVersion, Eq(SDK_BAKLAVA));
+
+  auto gingerbread_doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+        <TextView /><TextView />
+      </LinearLayout>)");
+
+  std::string expected0;
+  PrintDocToString(gingerbread_doc.get(), &expected0);
+  std::string actual0;
+  PrintDocToString(results[0].get(), &actual0);
+  EXPECT_THAT(actual0, Eq(expected0));
+
+  std::string expected1;
+  PrintDocToString(doc.get(), &expected1);
+  std::string actual1;
+  PrintDocToString(results[1].get(), &actual1);
+  EXPECT_THAT(actual1, Eq(expected1));
+}
+
+TEST_F(FlaggedXmlVersionerTest, NoVersionGetsSplit) {
+  auto doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+        <TextView android:featureFlag="package.flag" /><TextView /><TextView />
+      </LinearLayout>)");
+
+  FlaggedXmlVersioner versioner;
+  auto results = versioner.Process(context_.get(), doc.get());
+  EXPECT_THAT(results.size(), Eq(2));
+  EXPECT_THAT(results[0]->file.config.sdkVersion, Eq(0));
+  EXPECT_THAT(results[1]->file.config.sdkVersion, Eq(SDK_BAKLAVA));
+
+  auto gingerbread_doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+        <TextView /><TextView />
+      </LinearLayout>)");
+
+  std::string expected0;
+  PrintDocToString(gingerbread_doc.get(), &expected0);
+  std::string actual0;
+  PrintDocToString(results[0].get(), &actual0);
+  EXPECT_THAT(actual0, Eq(expected0));
+
+  std::string expected1;
+  PrintDocToString(doc.get(), &expected1);
+  std::string actual1;
+  PrintDocToString(results[1].get(), &actual1);
+  EXPECT_THAT(actual1, Eq(expected1));
+}
+
+TEST_F(FlaggedXmlVersionerTest, NegatedFlagAttributeRemoved) {
+  auto doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+        <TextView android:featureFlag="!package.flag" /><TextView /><TextView />
+      </LinearLayout>)");
+  doc->file.config.sdkVersion = SDK_GINGERBREAD;
+
+  FlaggedXmlVersioner versioner;
+  auto results = versioner.Process(context_.get(), doc.get());
+  EXPECT_THAT(results.size(), Eq(2));
+  EXPECT_THAT(results[0]->file.config.sdkVersion, Eq(SDK_GINGERBREAD));
+  EXPECT_THAT(results[1]->file.config.sdkVersion, Eq(SDK_BAKLAVA));
+
+  auto gingerbread_doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+        <TextView /><TextView /><TextView />
+      </LinearLayout>)");
+
+  std::string expected0;
+  PrintDocToString(gingerbread_doc.get(), &expected0);
+  std::string actual0;
+  PrintDocToString(results[0].get(), &actual0);
+  EXPECT_THAT(actual0, Eq(expected0));
+
+  std::string expected1;
+  PrintDocToString(doc.get(), &expected1);
+  std::string actual1;
+  PrintDocToString(results[1].get(), &actual1);
+  EXPECT_THAT(actual1, Eq(expected1));
+}
+
+TEST_F(FlaggedXmlVersionerTest, NegatedFlagAttributeRemovedNoSpecifiedVersion) {
+  auto doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+        <TextView android:featureFlag="!package.flag" /><TextView /><TextView />
+      </LinearLayout>)");
+
+  FlaggedXmlVersioner versioner;
+  auto results = versioner.Process(context_.get(), doc.get());
+  EXPECT_THAT(results.size(), Eq(2));
+  EXPECT_THAT(results[0]->file.config.sdkVersion, Eq(0));
+  EXPECT_THAT(results[1]->file.config.sdkVersion, Eq(SDK_BAKLAVA));
+
+  auto gingerbread_doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+        <TextView /><TextView /><TextView />
+      </LinearLayout>)");
+
+  std::string expected0;
+  PrintDocToString(gingerbread_doc.get(), &expected0);
+  std::string actual0;
+  PrintDocToString(results[0].get(), &actual0);
+  EXPECT_THAT(actual0, Eq(expected0));
+
+  std::string expected1;
+  PrintDocToString(doc.get(), &expected1);
+  std::string actual1;
+  PrintDocToString(results[1].get(), &actual1);
+  EXPECT_THAT(actual1, Eq(expected1));
+}
+
+}  // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
index ad676ca..789f6a0 100644
--- a/tools/aapt2/xml/XmlUtil.h
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -30,6 +30,7 @@
 constexpr const char* kSchemaAndroid = "http://schemas.android.com/apk/res/android";
 constexpr const char* kSchemaTools = "http://schemas.android.com/tools";
 constexpr const char* kSchemaAapt = "http://schemas.android.com/aapt";
+constexpr const char* kAttrFeatureFlag = "featureFlag";
 
 // Result of extracting a package name from a namespace URI declaration.
 struct ExtractedPackage {
diff --git a/tools/hiddenapi/OWNERS b/tools/hiddenapi/OWNERS
index dc82aac..d1e36b9 100644
--- a/tools/hiddenapi/OWNERS
+++ b/tools/hiddenapi/OWNERS
@@ -1,6 +1,5 @@
 # compat-team@ for changes to hiddenapi files
 mathewi@google.com
-satayev@google.com
 
 # soong-team@ as the files these tools protect are tightly coupled with Soong
 file:platform/build/soong:/OWNERS
diff --git a/tools/lint/OWNERS b/tools/lint/OWNERS
index 8e4569e..4035e19 100644
--- a/tools/lint/OWNERS
+++ b/tools/lint/OWNERS
@@ -1,6 +1,5 @@
 mattgilbride@google.com
 azharaa@google.com
-jsharkey@google.com
 
 per-file *CallingSettingsNonUserGetterMethods* = file:/packages/SettingsProvider/OWNERS
 per-file *RegisterReceiverFlagDetector* = jacobhobbie@google.com
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/OWNERS b/wifi/java/src/android/net/wifi/sharedconnectivity/OWNERS
index 2a4acc1..abb9aa4 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/OWNERS
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/OWNERS
@@ -1,4 +1,3 @@
 # Bug component: 1216021
 
 asapperstein@google.com
-etancohen@google.com