Merge "Modify minimize flicker test assertions." 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/api/current.txt b/core/api/current.txt
index 1312753..3da5a5c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -16993,7 +16993,7 @@
     method public void setFilterBitmap(boolean);
     method public void setFlags(int);
     method public void setFontFeatureSettings(String);
-    method @FlaggedApi("com.android.text.flags.typeface_redesign_readonly") public boolean setFontVariationOverride(@Nullable String);
+    method @FlaggedApi("com.android.text.flags.typeface_redesign_readonly") public void setFontVariationOverride(@Nullable String);
     method public boolean setFontVariationSettings(String);
     method public void setHinting(int);
     method public void setLetterSpacing(float);
@@ -23332,12 +23332,12 @@
   public static final class MediaCodecInfo.CodecCapabilities {
     ctor public MediaCodecInfo.CodecCapabilities();
     method public static android.media.MediaCodecInfo.CodecCapabilities createFromProfileLevel(String, int, int);
-    method public android.media.MediaCodecInfo.AudioCapabilities getAudioCapabilities();
+    method @Nullable public android.media.MediaCodecInfo.AudioCapabilities getAudioCapabilities();
     method public android.media.MediaFormat getDefaultFormat();
-    method public android.media.MediaCodecInfo.EncoderCapabilities getEncoderCapabilities();
+    method @Nullable public android.media.MediaCodecInfo.EncoderCapabilities getEncoderCapabilities();
     method public int getMaxSupportedInstances();
     method public String getMimeType();
-    method public android.media.MediaCodecInfo.VideoCapabilities getVideoCapabilities();
+    method @Nullable public android.media.MediaCodecInfo.VideoCapabilities getVideoCapabilities();
     method public boolean isFeatureRequired(String);
     method public boolean isFeatureSupported(String);
     method public boolean isFormatSupported(android.media.MediaFormat);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 514a582..00ec48b 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2128,6 +2128,12 @@
     method public android.media.PlaybackParams setAudioStretchMode(int);
   }
 
+  public class Utils {
+    ctor public Utils();
+    field public static final String SYNCHRONIZED_VIBRATION = "synchronized";
+    field public static final String VIBRATION_URI_PARAM = "vibration_uri";
+  }
+
   public final class VolumePolicy implements android.os.Parcelable {
     ctor public VolumePolicy(boolean, boolean, boolean, int);
     method public int describeContents();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 69d3e8d..f9ec214 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -550,35 +550,35 @@
     public static final int START_ASSISTANT_NOT_ACTIVE_SESSION = FIRST_START_FATAL_ERROR_CODE + 11;
 
     /**
-     * Result for IActivityManaqer.startActivity: the activity was started
+     * Result for IActivityManager.startActivity: the activity was started
      * successfully as normal.
      * @hide
      */
     public static final int START_SUCCESS = FIRST_START_SUCCESS_CODE;
 
     /**
-     * Result for IActivityManaqer.startActivity: the caller asked that the Intent not
+     * Result for IActivityManager.startActivity: the caller asked that the Intent not
      * be executed if it is the recipient, and that is indeed the case.
      * @hide
      */
     public static final int START_RETURN_INTENT_TO_CALLER = FIRST_START_SUCCESS_CODE + 1;
 
     /**
-     * Result for IActivityManaqer.startActivity: activity was started or brought forward in an
+     * Result for IActivityManager.startActivity: activity was started or brought forward in an
      * existing task which was brought to the foreground.
      * @hide
      */
     public static final int START_TASK_TO_FRONT = FIRST_START_SUCCESS_CODE + 2;
 
     /**
-     * Result for IActivityManaqer.startActivity: activity wasn't really started, but
+     * Result for IActivityManager.startActivity: activity wasn't really started, but
      * the given Intent was given to the existing top activity.
      * @hide
      */
     public static final int START_DELIVERED_TO_TOP = FIRST_START_SUCCESS_CODE + 3;
 
     /**
-     * Result for IActivityManaqer.startActivity: request was canceled because
+     * Result for IActivityManager.startActivity: request was canceled because
      * app switches are temporarily canceled to ensure the user's last request
      * (such as pressing home) is performed.
      * @hide
@@ -586,7 +586,7 @@
     public static final int START_SWITCHES_CANCELED = FIRST_START_NON_FATAL_ERROR_CODE;
 
     /**
-     * Result for IActivityManaqer.startActivity: a new activity was attempted to be started
+     * Result for IActivityManager.startActivity: a new activity was attempted to be started
      * while in Lock Task Mode.
      * @hide
      */
@@ -594,55 +594,55 @@
             FIRST_START_NON_FATAL_ERROR_CODE + 1;
 
     /**
-     * Result for IActivityManaqer.startActivity: a new activity start was aborted. Never returned
+     * Result for IActivityManager.startActivity: a new activity start was aborted. Never returned
      * externally.
      * @hide
      */
     public static final int START_ABORTED = FIRST_START_NON_FATAL_ERROR_CODE + 2;
 
     /**
-     * Flag for IActivityManaqer.startActivity: do special start mode where
+     * Flag for IActivityManager.startActivity: do special start mode where
      * a new activity is launched only if it is needed.
      * @hide
      */
     public static final int START_FLAG_ONLY_IF_NEEDED = 1<<0;
 
     /**
-     * Flag for IActivityManaqer.startActivity: launch the app for
+     * Flag for IActivityManager.startActivity: launch the app for
      * debugging.
      * @hide
      */
     public static final int START_FLAG_DEBUG = 1<<1;
 
     /**
-     * Flag for IActivityManaqer.startActivity: launch the app for
+     * Flag for IActivityManager.startActivity: launch the app for
      * allocation tracking.
      * @hide
      */
     public static final int START_FLAG_TRACK_ALLOCATION = 1<<2;
 
     /**
-     * Flag for IActivityManaqer.startActivity: launch the app with
+     * Flag for IActivityManager.startActivity: launch the app with
      * native debugging support.
      * @hide
      */
     public static final int START_FLAG_NATIVE_DEBUGGING = 1<<3;
 
     /**
-     * Flag for IActivityManaqer.startActivity: launch the app for
+     * Flag for IActivityManager.startActivity: launch the app for
      * debugging and suspend threads.
      * @hide
      */
     public static final int START_FLAG_DEBUG_SUSPEND = 1 << 4;
 
     /**
-     * Result for IActivityManaqer.broadcastIntent: success!
+     * Result for IActivityManager.broadcastIntent: success!
      * @hide
      */
     public static final int BROADCAST_SUCCESS = 0;
 
     /**
-     * Result for IActivityManaqer.broadcastIntent: attempt to broadcast
+     * Result for IActivityManager.broadcastIntent: attempt to broadcast
      * a sticky intent without appropriate permission.
      * @hide
      */
@@ -656,20 +656,20 @@
     public static final int BROADCAST_FAILED_USER_STOPPED = -2;
 
     /**
-     * Type for IActivityManaqer.getIntentSender: this PendingIntent type is unknown.
+     * Type for IActivityManager.getIntentSender: this PendingIntent type is unknown.
      * @hide
      */
     public static final int INTENT_SENDER_UNKNOWN = 0;
 
     /**
-     * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+     * Type for IActivityManager.getIntentSender: this PendingIntent is
      * for a sendBroadcast operation.
      * @hide
      */
     public static final int INTENT_SENDER_BROADCAST = 1;
 
     /**
-     * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+     * Type for IActivityManager.getIntentSender: this PendingIntent is
      * for a startActivity operation.
      * @hide
      */
@@ -677,21 +677,21 @@
     public static final int INTENT_SENDER_ACTIVITY = 2;
 
     /**
-     * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+     * Type for IActivityManager.getIntentSender: this PendingIntent is
      * for an activity result operation.
      * @hide
      */
     public static final int INTENT_SENDER_ACTIVITY_RESULT = 3;
 
     /**
-     * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+     * Type for IActivityManager.getIntentSender: this PendingIntent is
      * for a startService operation.
      * @hide
      */
     public static final int INTENT_SENDER_SERVICE = 4;
 
     /**
-     * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+     * Type for IActivityManager.getIntentSender: this PendingIntent is
      * for a startForegroundService operation.
      * @hide
      */
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1a6e9b0..dc5974f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -22,14 +22,12 @@
 import static android.permission.flags.Flags.shouldRegisterAttributionSource;
 import static android.view.WindowManager.LayoutParams.WindowType;
 
-import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.UiContext;
-import android.companion.virtual.VirtualDevice;
 import android.companion.virtual.VirtualDeviceManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.AttributionSource;
@@ -2367,47 +2365,11 @@
             Log.v(TAG, "Treating renounced permission " + permission + " as denied");
             return PERMISSION_DENIED;
         }
-        int deviceId = resolveDeviceIdForPermissionCheck(permission);
+        int deviceId = PermissionManager.resolveDeviceIdForPermissionCheck(this, getDeviceId(),
+                permission);
         return PermissionManager.checkPermission(permission, pid, uid, deviceId);
     }
 
-    private int resolveDeviceIdForPermissionCheck(String permission) {
-        // When checking a device-aware permission on a remote device, if the permission is CAMERA
-        // or RECORD_AUDIO we need to check remote device's corresponding capability. If the remote
-        // device doesn't have capability fall back to checking permission on the default device.
-        // Note: we only perform permission check redirection when the device id is not explicitly
-        // set in the context.
-        int deviceId = getDeviceId();
-        if (deviceId != Context.DEVICE_ID_DEFAULT
-                && !mIsExplicitDeviceId
-                && PermissionManager.DEVICE_AWARE_PERMISSIONS.contains(permission)) {
-            VirtualDeviceManager virtualDeviceManager =
-                    getSystemService(VirtualDeviceManager.class);
-            if (virtualDeviceManager == null) {
-                Slog.e(
-                        TAG,
-                        "VDM is not enabled when device id is not default. deviceId = "
-                                + deviceId);
-            } else {
-                VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
-                if (virtualDevice != null) {
-                    if ((Objects.equals(permission, Manifest.permission.RECORD_AUDIO)
-                            && !virtualDevice.hasCustomAudioInputSupport())
-                            || (Objects.equals(permission, Manifest.permission.CAMERA)
-                            && !virtualDevice.hasCustomCameraSupport())) {
-                        deviceId = Context.DEVICE_ID_DEFAULT;
-                    }
-                } else {
-                    Slog.e(
-                            TAG,
-                            "virtualDevice is not found when device id is not default. deviceId = "
-                                    + deviceId);
-                }
-            }
-        }
-        return deviceId;
-    }
-
     /** @hide */
     @Override
     public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
@@ -2511,7 +2473,8 @@
     @Override
     public int getPermissionRequestState(String permission) {
         Objects.requireNonNull(permission, "Permission name can't be null");
-        int deviceId = resolveDeviceIdForPermissionCheck(permission);
+        int deviceId = PermissionManager.resolveDeviceIdForPermissionCheck(this, getDeviceId(),
+                permission);
         PermissionManager permissionManager = getSystemService(PermissionManager.class);
         return permissionManager.getPermissionRequestState(getOpPackageName(), permission,
                 deviceId);
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/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index c573161..38141cf 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -293,6 +293,7 @@
 
     // The test mode. This is only used to ensure that the test functions setTestMode() and
     // testPropertyName() are used correctly.
+    @GuardedBy("sGlobalLock")
     private static boolean sTestMode = false;
 
     /**
@@ -668,7 +669,7 @@
         // True if this handler is in test mode.  If it is in test mode, then nonces are stored
         // and retrieved from mTestNonce.
         @GuardedBy("mLock")
-        private boolean mTestMode = false;
+        private boolean mTestMode;
 
         // This is the local value of the nonce, as last set by the NonceHandler.  It is always
         // updated by the setNonce() operation.  The getNonce() operation returns this value in
@@ -692,6 +693,9 @@
 
         NonceHandler(@NonNull String name) {
             mName = name;
+            synchronized (sGlobalLock) {
+                mTestMode = sTestMode;
+            }
         }
 
         /**
@@ -1414,9 +1418,13 @@
 
     /**
      * Enable or disable testing.  The protocol requires that the mode toggle: for instance, it is
-     * illegal to clear the test mode if the test mode is already off.  The purpose is solely to
-     * ensure that test clients do not forget to use the test mode properly, even though the
-     * current logic does not care.
+     * illegal to clear the test mode if the test mode is already off.  Enabling test mode puts
+     * all caches in the process into test mode; all nonces are initialized to UNSET and
+     * subsequent reads and writes are to process memory.  This has the effect of disabling all
+     * caches that are not local to the process.  Disabling test mode restores caches to normal
+     * operation.
+     * @param mode The desired test mode.
+     * @throws IllegalStateException if the supplied mode is already set.
      * @hide
      */
     @VisibleForTesting
@@ -1431,10 +1439,8 @@
                 }
             }
             sTestMode = mode;
-            if (mode) {
-                // No action when testing begins.
-            } else {
-                resetAfterTestLocked();
+            if (Flags.picTestMode() || !mode) {
+                setTestModeLocked(mode);
             }
         }
     }
@@ -1445,11 +1451,11 @@
      * that were not originally in test mode.
      */
     @GuardedBy("sGlobalLock")
-    private static void resetAfterTestLocked() {
+    private static void setTestModeLocked(boolean mode) {
         for (Iterator<String> e = sHandlers.keys().asIterator(); e.hasNext(); ) {
             String s = e.next();
             final NonceHandler h = sHandlers.get(s);
-            h.setTestMode(false);
+            h.setTestMode(mode);
         }
     }
 
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/performance.aconfig b/core/java/android/app/performance.aconfig
index 2569f7b..82875eb 100644
--- a/core/java/android/app/performance.aconfig
+++ b/core/java/android/app/performance.aconfig
@@ -50,3 +50,10 @@
      description: "Cache null returns from binder calls"
      bug: "372923336"
 }
+
+flag {
+     namespace: "system_performance"
+     name: "pic_test_mode"
+     description: "Updated test mode for PIC"
+     bug: "396173886"
+}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 6e3e86c..e726bc9 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -236,9 +236,17 @@
      * 2. The transaction message is scheduled.
      * 3. The client calls {@link TransactionExecutor#execute(ClientTransaction)}, which executes
      *    all callbacks and necessary lifecycle transitions.
+     *
+     * @return {@link RemoteException} if the transaction failed.
      */
-    public void schedule() throws RemoteException {
-        mClient.scheduleTransaction(this);
+    @Nullable
+    public RemoteException schedule() {
+        try {
+            mClient.scheduleTransaction(this);
+            return null;
+        } catch (RemoteException e) {
+            return e;
+        }
     }
 
     // Parcelable implementation
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/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 255a08c..42bef0e 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -400,3 +400,11 @@
     bug: "378539511"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "cloud_compilation_verification"
+    namespace: "art_mainline"
+    description: "Feature flag to enable the Cloud Compilation install-time verification in the package manager."
+    bug: "377474232"
+    is_fixed_read_only: true
+}
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/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/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index 240bc4f..2e7c3be 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -719,9 +719,13 @@
 
     /**
      * Enable or disable testing.  The protocol requires that the mode toggle: for instance, it is
-     * illegal to clear the test mode if the test mode is already off.  The purpose is solely to
-     * ensure that test clients do not forget to use the test mode properly, even though the
-     * current logic does not care.
+     * illegal to clear the test mode if the test mode is already off.  Enabling test mode puts
+     * all caches in the process into test mode; all nonces are initialized to UNSET and
+     * subsequent reads and writes are to process memory.  This has the effect of disabling all
+     * caches that are not local to the process.  Disabling test mode restores caches to normal
+     * operation.
+     * @param mode The desired test mode.
+     * @throws IllegalStateException if the supplied mode is already set.
      * @hide
      */
     @TestApi
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/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 5188204..561a2c9 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -16,6 +16,7 @@
 
 package android.permission;
 
+import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -2039,12 +2040,49 @@
                 new PackageNamePermissionQuery(permName, pkgName, persistentDeviceId, userId));
     }
 
+    /**
+     * When checking a device-aware permission on a remote device, if the permission is CAMERA
+     * or RECORD_AUDIO we need to check remote device's corresponding capability. If the remote
+     * device doesn't have capability fall back to checking permission on the default device.
+     *
+     * @hide
+     */
+    public static int resolveDeviceIdForPermissionCheck(@NonNull Context context, int deviceId,
+            @Nullable String permission) {
+        if (deviceId == Context.DEVICE_ID_DEFAULT || !DEVICE_AWARE_PERMISSIONS.contains(
+                permission)) {
+            return Context.DEVICE_ID_DEFAULT;
+        }
+
+        VirtualDeviceManager virtualDeviceManager =
+                context.getSystemService(VirtualDeviceManager.class);
+        if (virtualDeviceManager == null) {
+            Slog.e(LOG_TAG, "VDM is not enabled when device id is not default. deviceId = "
+                    + deviceId);
+        } else {
+            VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
+            if (virtualDevice != null) {
+                if ((Objects.equals(permission, Manifest.permission.RECORD_AUDIO)
+                        && !virtualDevice.hasCustomAudioInputSupport())
+                        || (Objects.equals(permission, Manifest.permission.CAMERA)
+                        && !virtualDevice.hasCustomCameraSupport())) {
+                    deviceId = Context.DEVICE_ID_DEFAULT;
+                }
+            } else {
+                Slog.e(LOG_TAG,
+                        "virtualDevice is not found when device id is not default. deviceId = "
+                                + deviceId);
+            }
+        }
+        return deviceId;
+    }
+
     @Nullable
     private String getPersistentDeviceId(int deviceId) {
         String persistentDeviceId = null;
 
         if (deviceId == Context.DEVICE_ID_DEFAULT) {
-            persistentDeviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
+            persistentDeviceId = PERSISTENT_DEVICE_ID_DEFAULT;
         } else {
             VirtualDeviceManager virtualDeviceManager = mContext.getSystemService(
                     VirtualDeviceManager.class);
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/View.java b/core/java/android/view/View.java
index c048d79..f50d77e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -34463,7 +34463,10 @@
      */
     @FlaggedApi(android.app.jank.Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
     public void reportAppJankStats(@NonNull AppJankStats appJankStats) {
-        getRootView().reportAppJankStats(appJankStats);
+        View rootView = getRootView();
+        if (rootView == this) return;
+
+        rootView.reportAppJankStats(appJankStats);
     }
 
     /**
@@ -34471,6 +34474,10 @@
      * @hide
      */
     public @Nullable JankTracker getJankTracker() {
-        return getRootView().getJankTracker();
+        View rootView = getRootView();
+        if (rootView == this) {
+            return null;
+        }
+        return rootView.getJankTracker();
     }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7fd7be8..e157da7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -153,6 +153,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.ResourcesManager;
+import android.app.UiModeManager;
 import android.app.WindowConfiguration;
 import android.app.compat.CompatChanges;
 import android.app.servertransaction.WindowStateTransactionItem;
@@ -196,6 +197,7 @@
 import android.hardware.input.InputManagerGlobal;
 import android.hardware.input.InputSettings;
 import android.media.AudioManager;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -472,8 +474,6 @@
     @Nullable
     private ContentObserver mForceInvertObserver;
 
-    private static final int INVALID_VALUE = Integer.MIN_VALUE;
-    private int mForceInvertEnabled = INVALID_VALUE;
     /**
      * Callback for notifying about global configuration changes.
      */
@@ -555,6 +555,8 @@
     @UiContext
     public final Context mContext;
 
+    private UiModeManager mUiModeManager;
+
     @UnsupportedAppUsage
     final IWindowSession mWindowSession;
     @NonNull Display mDisplay;
@@ -1804,23 +1806,6 @@
         }
     }
 
-    private boolean isForceInvertEnabled() {
-        if (mForceInvertEnabled == INVALID_VALUE) {
-            reloadForceInvertEnabled();
-        }
-        return mForceInvertEnabled == 1;
-    }
-
-    private void reloadForceInvertEnabled() {
-        if (forceInvertColor()) {
-            mForceInvertEnabled = Settings.Secure.getIntForUser(
-                    mContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
-                    /* def= */ 0,
-                    UserHandle.myUserId());
-        }
-    }
-
     /**
      * Register any kind of listeners if setView was success.
      */
@@ -1856,17 +1841,22 @@
                 mForceInvertObserver = new ContentObserver(mHandler) {
                     @Override
                     public void onChange(boolean selfChange) {
-                        reloadForceInvertEnabled();
                         updateForceDarkMode();
                     }
                 };
-                mContext.getContentResolver().registerContentObserver(
-                        Settings.Secure.getUriFor(
-                                Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED
-                        ),
-                        false,
-                        mForceInvertObserver,
-                        UserHandle.myUserId());
+
+                final Uri[] urisToObserve = {
+                    Settings.Secure.getUriFor(
+                        Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED),
+                    Settings.Secure.getUriFor(Settings.Secure.UI_NIGHT_MODE)
+                };
+                for (Uri uri : urisToObserve) {
+                    mContext.getContentResolver().registerContentObserver(
+                            uri,
+                            false,
+                            mForceInvertObserver,
+                            UserHandle.myUserId());
+                }
             }
         }
     }
@@ -2073,21 +2063,25 @@
         return getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
     }
 
-    /** Returns true if force dark should be enabled according to various settings */
+    /**
+     * Determines the type of force dark to apply, considering force inversion, system night mode,
+     * and app-specific settings (including developer opt-outs).
+     *
+     * @return A {@link ForceDarkType.ForceDarkTypeDef} constant indicating the force dark type.
+     */
     @VisibleForTesting
     public @ForceDarkType.ForceDarkTypeDef int determineForceDarkType() {
         if (forceInvertColor()) {
             // Force invert ignores all developer opt-outs.
             // We also ignore dark theme, since the app developer can override the user's preference
-            // for dark mode in configuration.uiMode. Instead, we assume that the force invert
-            // setting will be enabled at the same time dark theme is in the Settings app.
-            if (isForceInvertEnabled()) {
+            // for dark mode in configuration.uiMode. Instead, we assume that both force invert and
+            // the system's dark theme are enabled.
+            if (getUiModeManager().getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK) {
                 return ForceDarkType.FORCE_INVERT_COLOR_DARK;
             }
         }
 
         boolean useAutoDark = getNightMode() == Configuration.UI_MODE_NIGHT_YES;
-
         if (useAutoDark) {
             boolean forceDarkAllowedDefault =
                     SystemProperties.getBoolean(ThreadedRenderer.DEBUG_FORCE_DARK, false);
@@ -9401,6 +9395,13 @@
         return mAudioManager;
     }
 
+    private UiModeManager getUiModeManager() {
+        if (mUiModeManager == null) {
+            mUiModeManager = mContext.getSystemService(UiModeManager.class);
+        }
+        return mUiModeManager;
+    }
+
     private Vibrator getSystemVibrator() {
         if (mVibrator == null) {
             mVibrator = mContext.getSystemService(Vibrator.class);
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/WindowManager.java b/core/java/android/view/WindowManager.java
index 196ae5e..83dc79b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -631,6 +631,12 @@
     int TRANSIT_FLAG_AOD_APPEARING = (1 << 15); // 0x8000
 
     /**
+     * Transition flag: Indicates that the task shouldn't move to front when launching the activity.
+     * @hide
+     */
+    int TRANSIT_FLAG_AVOID_MOVE_TO_FRONT = (1 << 16); // 0x10000
+
+    /**
      * @hide
      */
     @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
@@ -650,6 +656,7 @@
             TRANSIT_FLAG_KEYGUARD_UNOCCLUDING,
             TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH,
             TRANSIT_FLAG_AOD_APPEARING,
+            TRANSIT_FLAG_AVOID_MOVE_TO_FRONT,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface TransitionFlags {}
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/webkit/TEST_MAPPING b/core/java/android/webkit/TEST_MAPPING
index 3858059..c9b5476 100644
--- a/core/java/android/webkit/TEST_MAPPING
+++ b/core/java/android/webkit/TEST_MAPPING
@@ -17,15 +17,6 @@
           "exclude-annotation": "android.test.FlakyTest"
         }
       ]
-    },
-    {
-      "name": "GtsWebViewHostTestCases",
-      "keywords": ["internal"],
-      "options": [
-        {
-          "exclude-annotation": "android.test.FlakyTest"
-        }
-      ]
     }
   ]
 }
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 14b208a..ab7a4f2 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1458,13 +1458,14 @@
     public abstract void setNeedInitialFocus(boolean flag);
 
     /**
-     * Sets the priority of the Render thread. Unlike the other settings, this
+     * Sets the CPU scheduling priority of the Render thread. Unlike the other settings, this
      * one only needs to be called once per process. The default value is
      * {@link RenderPriority#NORMAL}.
      *
      * @param priority the priority
-     * @deprecated It is not recommended to adjust thread priorities, and this will
-     *             not be supported in future versions.
+     * @deprecated This is no longer supported. See {@link WebView#setRendererPriorityPolicy} if you
+     *             instead want to control how freely the system should kill the renderer process
+     *             under low memory conditions.
      */
     @Deprecated
     public abstract void setRenderPriority(RenderPriority priority);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index b666399..5e22c28 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2287,26 +2287,53 @@
     public @interface RendererPriority {}
 
     /**
-     * The renderer associated with this WebView is bound with
-     * {@link Context#BIND_WAIVE_PRIORITY}. At this priority level
-     * {@link WebView} renderers will be strong targets for out of memory
-     * killing.
+     * This is the lowest binding priority for the WebView renderer process. This is equivalent to
+     * {@link Context#BIND_WAIVE_PRIORITY}. At this priority level {@link WebView} renderers will be
+     * frequent targets for being killed when the system is running low on memory.
      *
-     * Use with {@link #setRendererPriorityPolicy}.
+     * <p>If using this priority, we recommend handling the {@link
+     * WebViewClient#onRenderProcessGone} callback to recover from low memory kills as well as other
+     * types of renderer crashes.
+     *
+     * @see #setRendererPriorityPolicy
+     * @see #getRendererPriorityPolicy
+     * @see RenderProcessGoneDetail#rendererPriorityAtExit
      */
     public static final int RENDERER_PRIORITY_WAIVED = 0;
+
     /**
-     * The renderer associated with this WebView is bound with
-     * the default priority for services.
+     * This is the medium binding priority for the WebView renderer process. This is equivalent to
+     * the standard priority used by the system for calls to {@link
+     * Context#bindService(Intent,android.content.ServiceConnection,int)} when no flags are
+     * provided. At this priority level {@link WebView} renderers will be slightly more likely
+     * targets for being killed when the system is running low on memory.
      *
-     * Use with {@link #setRendererPriorityPolicy}.
+     * <p>If using this priority, we recommend handling the {@link
+     * WebViewClient#onRenderProcessGone} callback to recover from low memory kills as well as other
+     * types of renderer crashes.
+     *
+     * @see #setRendererPriorityPolicy
+     * @see #getRendererPriorityPolicy
+     * @see RenderProcessGoneDetail#rendererPriorityAtExit
      */
     public static final int RENDERER_PRIORITY_BOUND = 1;
+
     /**
-     * The renderer associated with this WebView is bound with
-     * {@link Context#BIND_IMPORTANT}.
+     * This is the highest binding priority for the WebView renderer process, and the default value
+     * WebView uses to bind to the renderer process. This is equivalent to {@link
+     * Context#BIND_IMPORTANT}. At this priority level {@link WebView} renderers are less likely to
+     * be killed when the system is running low on memory and will have the same priority as your
+     * app's main process.
      *
-     * Use with {@link #setRendererPriorityPolicy}.
+     * <p>It's still possible for the renderer process to be killed when the system is running low
+     * on memory, however specifying this priority makes this situation less likely. It's also still
+     * possible for the renderer process to crash for other reasons. You may optionally still choose
+     * to handle {@link WebViewClient#onRenderProcessGone} in order to recover from a killed or
+     * crashed renderer process.
+     *
+     * @see #setRendererPriorityPolicy
+     * @see #getRendererPriorityPolicy
+     * @see RenderProcessGoneDetail#rendererPriorityAtExit
      */
     public static final int RENDERER_PRIORITY_IMPORTANT = 2;
 
@@ -2316,7 +2343,7 @@
      * process renderer should be considered to be a target for OOM
      * killing.
      *
-     * Because a renderer can be associated with more than one
+     * <p>Because a renderer can be associated with more than one
      * WebView, the final priority it is computed as the maximum of
      * any attached WebViews. When a WebView is destroyed it will
      * cease to be considerered when calculating the renderer
@@ -2324,7 +2351,7 @@
      * the priority of the renderer will be reduced to
      * {@link #RENDERER_PRIORITY_WAIVED}.
      *
-     * The default policy is to set the priority to
+     * <p>The default policy is to set the priority to
      * {@link #RENDERER_PRIORITY_IMPORTANT} regardless of visibility,
      * and this should not be changed unless the caller also handles
      * renderer crashes with
@@ -2338,6 +2365,8 @@
      *        when this WebView is not visible, it will be treated as
      *        if it had requested a priority of
      *        {@link #RENDERER_PRIORITY_WAIVED}.
+     * @see #getRendererPriorityPolicy
+     * @see RenderProcessGoneDetail#rendererPriorityAtExit
      */
     public void setRendererPriorityPolicy(
             @RendererPriority int rendererRequestedPriority,
@@ -2349,6 +2378,8 @@
      * Get the requested renderer priority for this WebView.
      *
      * @return the requested renderer priority policy.
+     * @see #setRendererPriorityPolicy
+     * @see RenderProcessGoneDetail#rendererPriorityAtExit
      */
     @InspectableProperty(hasAttributeId = false, enumMapping = {
             @InspectableProperty.EnumEntry(name = "waived", value = RENDERER_PRIORITY_WAIVED),
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 0fdc776..3cdf290 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),
@@ -59,6 +61,8 @@
     ENABLE_DESKTOP_COMPAT_UI_VISIBILITY_STATUS(Flags::enableCompatUiVisibilityStatus, true),
     ENABLE_DESKTOP_INDICATOR_IN_SEPARATE_THREAD_BUGFIX(
             Flags::enableDesktopIndicatorInSeparateThreadBugfix, false),
+    ENABLE_DESKTOP_OPENING_DEEPLINK_MINIMIZE_ANIMATION_BUGFIX(
+            Flags::enableDesktopOpeningDeeplinkMinimizeAnimationBugfix, false),
     ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX(
             Flags::enableDesktopRecentsTransitionsCornersBugfix, false),
     ENABLE_DESKTOP_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE_BUGFIX(
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 09eae28..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"
@@ -831,4 +842,14 @@
     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_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 9f768f0..f2efa20 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -152,4 +152,15 @@
     metadata {
         purpose: PURPOSE_BUGFIX
     }
-}
\ No newline at end of file
+}
+
+flag {
+    namespace: "windowing_sdk"
+    name: "cleanup_dispatch_pending_transactions_remote_exception"
+    description: "Refactor to cleanup for RemoteException from dispatchPendingTransactions"
+    bug: "323801078"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
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/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 453a075..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();
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index f2099bc..9fe2de8 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -63,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();
@@ -223,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);
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/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index ac78e87..f75a72d 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -414,16 +414,23 @@
     @Test
     @DisabledOnRavenwood(reason = "SystemProperties doesn't have permission check")
     public void testPermissionFailure() {
-        // Create a cache that will write a system nonce.
-        TestCache sysCache = new TestCache(MODULE_SYSTEM, "mode1");
         try {
-            // Invalidate the cache, which writes the system property.  There must be a permission
-            // failure.
-            sysCache.invalidateCache();
-            fail("expected permission failure");
-        } catch (RuntimeException e) {
-            // The expected exception is a bare RuntimeException.  The test does not attempt to
-            // validate the text of the exception message.
+            // Disable the test mode for this test, but ensure that it will be enabled when the
+            // test exits.
+            PropertyInvalidatedCache.setTestMode(false);
+            // Create a cache that will write a system nonce.
+            TestCache sysCache = new TestCache(MODULE_SYSTEM, "mode1");
+            try {
+                // Invalidate the cache, which writes the system property.  There must be a
+                // permission failure.
+                sysCache.invalidateCache();
+                fail("expected permission failure");
+            } catch (RuntimeException e) {
+                // The expected exception is a bare RuntimeException.  The test does not attempt
+                // to validate the text of the exception message.
+            }
+        } finally {
+            PropertyInvalidatedCache.setTestMode(true);
         }
     }
 
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/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
index 74b32a1..791ec5d 100644
--- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -452,22 +452,28 @@
         assertTrue(ec.isDisabled());
     }
 
-
     // Verify that invalidating the cache from an app process would fail due to lack of permissions.
     @Test
     @android.platform.test.annotations.DisabledOnRavenwood(
             reason = "SystemProperties doesn't have permission check")
     public void testPermissionFailure() {
-        // Create a cache that will write a system nonce.
-        TestCache sysCache = new TestCache(IpcDataCache.MODULE_SYSTEM, "mode1");
         try {
-            // Invalidate the cache, which writes the system property.  There must be a permission
-            // failure.
-            sysCache.invalidateCache();
-            fail("expected permission failure");
-        } catch (RuntimeException e) {
-            // The expected exception is a bare RuntimeException.  The test does not attempt to
-            // validate the text of the exception message.
+            // Disable test mode for this test.  Guarantee that the mode is enabled before the
+            // test exits.
+            IpcDataCache.setTestMode(false);
+            // Create a cache that will write a system nonce.
+            TestCache sysCache = new TestCache(IpcDataCache.MODULE_SYSTEM, "mode1");
+            try {
+                // Invalidate the cache, which writes the system property.  There must be a
+                // permission failure.
+                sysCache.invalidateCache();
+                fail("expected permission failure");
+            } catch (RuntimeException e) {
+                // The expected exception is a bare RuntimeException.  The test does not attempt
+                // to validate the text of the exception message.
+            }
+        } finally {
+            IpcDataCache.setTestMode(true);
         }
     }
 
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index f5d1e7a..39f3d33 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static android.app.UiModeManager.MODE_NIGHT_NO;
+import static android.app.UiModeManager.MODE_NIGHT_YES;
 import static android.util.SequenceUtils.getInitSeq;
 import static android.view.HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING;
 import static android.view.InputDevice.SOURCE_ROTARY_ENCODER;
@@ -67,8 +69,10 @@
 import android.annotation.NonNull;
 import android.app.Instrumentation;
 import android.app.UiModeManager;
+import android.app.UiModeManager.ForceInvertType;
 import android.content.Context;
 import android.graphics.ForceDarkType;
+import android.graphics.ForceDarkType.ForceDarkTypeDef;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Binder;
@@ -93,9 +97,12 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.compatibility.common.util.TestUtils;
 import com.android.cts.input.BlockingQueueEventVerifier;
 import com.android.window.flags.Flags;
 
+import com.google.common.truth.Expect;
+
 import org.hamcrest.Matcher;
 import org.junit.After;
 import org.junit.AfterClass;
@@ -124,6 +131,8 @@
 
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    @Rule
+    public final Expect mExpect = Expect.create();
 
     private ViewRootImpl mViewRootImpl;
     private View mView;
@@ -1507,49 +1516,34 @@
     }
 
     @Test
-    public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
-        mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
-        ShellIdentityUtils.invokeWithShellPermissions(() -> {
-            Settings.Secure.putInt(
-                    sContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
-                    /* value= */ 0
-            );
-            var uiModeManager = sContext.getSystemService(UiModeManager.class);
-            uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
-        });
+    @RequiresFlagsEnabled(FLAG_FORCE_INVERT_COLOR)
+    public void updateConfiguration_returnsExpectedForceDarkMode() {
+        waitForSystemNightModeActivated(true);
 
-        sInstrumentation.runOnMainSync(() ->
-                mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
-        );
+        verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ true,
+                UiModeManager.FORCE_INVERT_TYPE_DARK, ForceDarkType.FORCE_INVERT_COLOR_DARK);
+        verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ false,
+                UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+        verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ true,
+                UiModeManager.FORCE_INVERT_TYPE_DARK, ForceDarkType.FORCE_INVERT_COLOR_DARK);
+        verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ false,
+                UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
 
-        assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE);
+        waitForSystemNightModeActivated(false);
+
+        verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ true,
+                UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+        verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ false,
+                UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+        verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ true,
+                UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+        verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ false,
+                UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
     }
 
     @Test
-    public void forceInvertOnDarkThemeOff_forceDarkModeEnabled() {
-        mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
-        ShellIdentityUtils.invokeWithShellPermissions(() -> {
-            Settings.Secure.putInt(
-                    sContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
-                    /* value= */ 1
-            );
-            var uiModeManager = sContext.getSystemService(UiModeManager.class);
-            uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
-        });
-
-        sInstrumentation.runOnMainSync(() ->
-                mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
-        );
-
-        assertThat(mViewRootImpl.determineForceDarkType())
-                .isEqualTo(ForceDarkType.FORCE_INVERT_COLOR_DARK);
-    }
-
-    @Test
+    @EnableFlags(FLAG_FORCE_INVERT_COLOR)
     public void forceInvertOffForceDarkOff_forceDarkModeDisabled() {
-        mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
         ShellIdentityUtils.invokeWithShellPermissions(() -> {
             Settings.Secure.putInt(
                     sContext.getContentResolver(),
@@ -1562,15 +1556,14 @@
         });
 
         sInstrumentation.runOnMainSync(() ->
-                mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
-        );
+                mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()));
 
         assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE);
     }
 
     @Test
+    @EnableFlags(FLAG_FORCE_INVERT_COLOR)
     public void forceInvertOffForceDarkOn_forceDarkModeEnabled() {
-        mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
         ShellIdentityUtils.invokeWithShellPermissions(() -> {
             Settings.Secure.putInt(
                     sContext.getContentResolver(),
@@ -1582,8 +1575,7 @@
         });
 
         sInstrumentation.runOnMainSync(() ->
-                mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
-        );
+                mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()));
 
         assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.FORCE_DARK);
     }
@@ -1790,4 +1782,39 @@
                     () -> view.getViewTreeObserver().removeOnDrawListener(listener));
         }
     }
+
+    private void waitForSystemNightModeActivated(boolean active) {
+        ShellIdentityUtils.invokeWithShellPermissions(() ->
+                sInstrumentation.runOnMainSync(() -> {
+                    var uiModeManager = sContext.getSystemService(UiModeManager.class);
+                    uiModeManager.setNightModeActivated(active);
+                }));
+        sInstrumentation.waitForIdleSync();
+    }
+
+    private void verifyForceDarkType(boolean isAppInNightMode, boolean isForceInvertEnabled,
+            @ForceInvertType int expectedForceInvertType,
+            @ForceDarkTypeDef int expectedForceDarkType) {
+        var uiModeManager = sContext.getSystemService(UiModeManager.class);
+        ShellIdentityUtils.invokeWithShellPermissions(() -> {
+            uiModeManager.setApplicationNightMode(
+                    isAppInNightMode ? MODE_NIGHT_YES : MODE_NIGHT_NO);
+            Settings.Secure.putInt(
+                    sContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
+                    isForceInvertEnabled ? 1 : 0);
+        });
+
+        sInstrumentation.runOnMainSync(() ->
+                mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()));
+        try {
+            TestUtils.waitUntil("Waiting for force invert state changed",
+                    () -> (uiModeManager.getForceInvertState() == expectedForceInvertType));
+        } catch (Exception e) {
+            Log.e(TAG, "Unexpected error trying to apply force invert state. " + e);
+            e.printStackTrace();
+        }
+
+        mExpect.that(mViewRootImpl.determineForceDarkType()).isEqualTo(expectedForceDarkType);
+    }
 }
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/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/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index a0ca098..05e23a0 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2206,26 +2206,21 @@
      * @param fontVariationOverride font variation override. You can pass null or empty string for
      *                              clearing font variation override.
      *
-     * @return true if the provided font variation settings is valid. Otherwise returns false.
-     *
+     * @throws IllegalArgumentException If given string is not a valid font variation settings
+     *                                  format
      * @see #getFontVariationSettings()
      * @see #setFontVariationSettings(String)
      * @see #getFontVariationOverride()
      * @see FontVariationAxis
      */
     @FlaggedApi(FLAG_TYPEFACE_REDESIGN_READONLY)
-    public boolean setFontVariationOverride(@Nullable String fontVariationOverride) {
+    public void setFontVariationOverride(@Nullable String fontVariationOverride) {
         if (Objects.equals(fontVariationOverride, mFontVariationOverride)) {
-            return true;
+            return;
         }
 
-        List<FontVariationAxis> axes;
-        try {
-            axes = FontVariationAxis.fromFontVariationSettingsForList(fontVariationOverride);
-        } catch (IllegalArgumentException e) {
-            Log.i(TAG, "failed to parse font variation settings.", e);
-            return false;
-        }
+        List<FontVariationAxis> axes =
+                FontVariationAxis.fromFontVariationSettingsForList(fontVariationOverride);
         long builderPtr = nCreateFontVariationBuilder(axes.size());
         for (int i = 0; i < axes.size(); ++i) {
             FontVariationAxis axis = axes.get(i);
@@ -2234,7 +2229,6 @@
         }
         nSetFontVariationOverride(mNativePaint, builderPtr);
         mFontVariationOverride = fontVariationOverride;
-        return true;
     }
 
     /**
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/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 305fcdd..be22402 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -117,6 +117,7 @@
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.UpdateSource;
 import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
 import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider;
 import com.android.wm.shell.shared.bubbles.DeviceConfig;
@@ -795,7 +796,7 @@
      * Update bubble bar location and trigger and update to listeners
      */
     public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation,
-            @BubbleBarLocation.UpdateSource int source) {
+            @UpdateSource int source) {
         if (isShowingAsBubbleBar()) {
             updateExpandedViewForBubbleBarLocation(bubbleBarLocation, source);
             BubbleBarUpdate bubbleBarUpdate = new BubbleBarUpdate();
@@ -805,7 +806,7 @@
     }
 
     private void updateExpandedViewForBubbleBarLocation(BubbleBarLocation bubbleBarLocation,
-            @BubbleBarLocation.UpdateSource int source) {
+            @UpdateSource int source) {
         if (isShowingAsBubbleBar()) {
             BubbleBarLocation previousLocation = mBubblePositioner.getBubbleBarLocation();
             mBubblePositioner.setBubbleBarLocation(bubbleBarLocation);
@@ -818,7 +819,7 @@
 
     private void logBubbleBarLocationIfChanged(BubbleBarLocation location,
             BubbleBarLocation previous,
-            @BubbleBarLocation.UpdateSource int source) {
+            @UpdateSource int source) {
         if (mLayerView == null) {
             return;
         }
@@ -830,25 +831,25 @@
             return;
         }
         switch (source) {
-            case BubbleBarLocation.UpdateSource.DRAG_BAR:
-            case BubbleBarLocation.UpdateSource.A11Y_ACTION_BAR:
+            case UpdateSource.DRAG_BAR:
+            case UpdateSource.A11Y_ACTION_BAR:
                 mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BAR
                         : BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BAR);
                 break;
-            case BubbleBarLocation.UpdateSource.DRAG_BUBBLE:
-            case BubbleBarLocation.UpdateSource.A11Y_ACTION_BUBBLE:
+            case UpdateSource.DRAG_BUBBLE:
+            case UpdateSource.A11Y_ACTION_BUBBLE:
                 mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BUBBLE
                         : BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BUBBLE);
                 break;
-            case BubbleBarLocation.UpdateSource.DRAG_EXP_VIEW:
-            case BubbleBarLocation.UpdateSource.A11Y_ACTION_EXP_VIEW:
+            case UpdateSource.DRAG_EXP_VIEW:
+            case UpdateSource.A11Y_ACTION_EXP_VIEW:
                 // TODO(b/349845968): move logging from BubbleBarLayerView to here
                 break;
-            case BubbleBarLocation.UpdateSource.APP_ICON_DRAG:
+            case UpdateSource.APP_ICON_DRAG:
                 mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_APP_ICON_DROP
                         : BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_APP_ICON_DROP);
                 break;
-            case BubbleBarLocation.UpdateSource.DRAG_TASK:
+            case UpdateSource.DRAG_TASK:
                 mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_TASK
                         : BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_TASK);
                 break;
@@ -872,10 +873,7 @@
         if (bubbleBarLocation == null) return;
         if (isShowingAsBubbleBar() && BubbleAnythingFlagHelper.enableCreateAnyBubble()) {
             mBubbleStateListener.onDragItemOverBubbleBarDragZone(bubbleBarLocation);
-            ensureBubbleViewsAndWindowCreated();
-            if (mLayerView != null) {
-                mLayerView.showBubbleBarExtendedViewDropTarget(bubbleBarLocation);
-            }
+            showBubbleBarExpandedViewDropTarget(bubbleBarLocation);
         }
     }
 
@@ -921,6 +919,13 @@
         return result;
     }
 
+    private void showBubbleBarExpandedViewDropTarget(BubbleBarLocation bubbleBarLocation) {
+        ensureBubbleViewsAndWindowCreated();
+        if (mLayerView != null) {
+            mLayerView.showBubbleBarExtendedViewDropTarget(bubbleBarLocation);
+        }
+    }
+
     private void hideBubbleBarExpandedViewDropTarget() {
         if (mLayerView != null) {
             mLayerView.hideBubbleBarExpandedViewDropTarget();
@@ -1541,20 +1546,9 @@
     public void expandStackAndSelectBubble(ShortcutInfo info,
             @Nullable BubbleBarLocation bubbleBarLocation) {
         if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
-        BubbleBarLocation updateLocation = isShowingAsBubbleBar() ? bubbleBarLocation : null;
-        if (updateLocation != null) {
-            updateExpandedViewForBubbleBarLocation(updateLocation,
-                    BubbleBarLocation.UpdateSource.APP_ICON_DRAG);
-        }
         Bubble b = mBubbleData.getOrCreateBubble(info); // Removes from overflow
         ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - shortcut=%s", info);
-        if (b.isInflated()) {
-            mBubbleData.setSelectedBubbleAndExpandStack(b, updateLocation);
-        } else {
-            b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
-            inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false,
-                    updateLocation);
-        }
+        expandStackAndSelectAppBubble(b, bubbleBarLocation, UpdateSource.APP_ICON_DRAG);
     }
 
     /**
@@ -1562,16 +1556,12 @@
      *
      * @param intent the intent for the bubble.
      */
-    public void expandStackAndSelectBubble(Intent intent, UserHandle user) {
+    public void expandStackAndSelectBubble(Intent intent, UserHandle user,
+            @Nullable BubbleBarLocation bubbleBarLocation) {
         if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
         Bubble b = mBubbleData.getOrCreateBubble(intent, user); // Removes from overflow
         ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - intent=%s", intent);
-        if (b.isInflated()) {
-            mBubbleData.setSelectedBubbleAndExpandStack(b);
-        } else {
-            b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
-            inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
-        }
+        expandStackAndSelectAppBubble(b, bubbleBarLocation, UpdateSource.APP_ICON_DRAG);
     }
 
     /**
@@ -1583,14 +1573,19 @@
     public void expandStackAndSelectBubble(PendingIntent pendingIntent, UserHandle user,
             @Nullable BubbleBarLocation bubbleBarLocation) {
         if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
-        BubbleBarLocation updateLocation = isShowingAsBubbleBar() ? bubbleBarLocation : null;
-        if (updateLocation != null) {
-            updateExpandedViewForBubbleBarLocation(updateLocation,
-                    BubbleBarLocation.UpdateSource.APP_ICON_DRAG);
-        }
-        Bubble b = mBubbleData.getOrCreateBubble(pendingIntent, user);
+        Bubble b = mBubbleData.getOrCreateBubble(pendingIntent, user); // Removes from overflow
         ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - pendingIntent=%s",
                 pendingIntent);
+        expandStackAndSelectAppBubble(b, bubbleBarLocation, UpdateSource.APP_ICON_DRAG);
+    }
+
+    private void expandStackAndSelectAppBubble(Bubble b,
+            @Nullable BubbleBarLocation bubbleBarLocation, @UpdateSource int source) {
+        if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
+        BubbleBarLocation updateLocation = isShowingAsBubbleBar() ? bubbleBarLocation : null;
+        if (updateLocation != null) {
+            updateExpandedViewForBubbleBarLocation(updateLocation, source);
+        }
         if (b.isInflated()) {
             mBubbleData.setSelectedBubbleAndExpandStack(b, updateLocation);
         } else {
@@ -1623,7 +1618,7 @@
             }
         } else {
             if (location != null) {
-                setBubbleBarLocation(location, BubbleBarLocation.UpdateSource.DRAG_TASK);
+                setBubbleBarLocation(location, UpdateSource.DRAG_TASK);
             }
             b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
             // Lazy init stack view when a bubble is created
@@ -2840,14 +2835,16 @@
         }
 
         @Override
-        public void showShortcutBubble(ShortcutInfo info) {
+        public void showShortcutBubble(ShortcutInfo info, @Nullable BubbleBarLocation location) {
             mMainExecutor.execute(() -> mController
-                    .expandStackAndSelectBubble(info, /* bubbleBarLocation = */ null));
+                    .expandStackAndSelectBubble(info, location));
         }
 
         @Override
-        public void showAppBubble(Intent intent, UserHandle user) {
-            mMainExecutor.execute(() -> mController.expandStackAndSelectBubble(intent, user));
+        public void showAppBubble(Intent intent, UserHandle user,
+                @Nullable BubbleBarLocation location) {
+            mMainExecutor.execute(
+                    () -> mController.expandStackAndSelectBubble(intent, user, location));
         }
 
         @Override
@@ -2900,7 +2897,7 @@
 
         @Override
         public void setBubbleBarLocation(BubbleBarLocation location,
-                @BubbleBarLocation.UpdateSource int source) {
+                @UpdateSource int source) {
             mMainExecutor.execute(() ->
                     mController.setBubbleBarLocation(location, source));
         }
@@ -2921,6 +2918,17 @@
                 }
             });
         }
+
+        @Override
+        public void showDropTarget(boolean show, BubbleBarLocation location) {
+            mMainExecutor.execute(() -> {
+                if (show) {
+                    showBubbleBarExpandedViewDropTarget(location);
+                } else {
+                    hideBubbleBarExpandedViewDropTarget();
+                }
+            });
+        }
     }
 
     private class BubblesImpl implements Bubbles {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 0a4d79a..ae1b407 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -51,9 +51,11 @@
 
     oneway void stopBubbleDrag(in BubbleBarLocation location, in int topOnScreen) = 11;
 
-    oneway void showShortcutBubble(in ShortcutInfo info) = 12;
+    oneway void showShortcutBubble(in ShortcutInfo info, in @nullable BubbleBarLocation location) = 12;
 
-    oneway void showAppBubble(in Intent intent, in UserHandle user) = 13;
+    oneway void showAppBubble(in Intent intent, in UserHandle user, in @nullable BubbleBarLocation location) = 13;
 
     oneway void showExpandedView() = 14;
+
+    oneway void showDropTarget(in boolean show, in @nullable BubbleBarLocation location) = 15;
 }
\ No newline at end of file
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/DesktopTaskChangeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
index 6034299..70a648f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
@@ -35,7 +35,7 @@
             desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
             return
         }
-        if (isFreeformTask(taskInfo)) {
+        if (isFreeformTask(taskInfo) && !desktopRepository.isActiveTask(taskInfo.taskId)) {
             desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
         }
     }
@@ -44,23 +44,33 @@
         logD("onTaskChanging for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
         val desktopRepository: DesktopRepository =
             desktopUserRepositories.getProfile(taskInfo.userId)
-        if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
-
         // TODO: b/394281403 - with multiple desks, it's possible to have a non-freeform task
         //  inside a desk, so this should be decoupled from windowing mode.
         //  Also, changes in/out of desks are handled by the [DesksTransitionObserver], which has
         //  more specific information about the desk involved in the transition, which might be
         //  more accurate than assuming it's always the default/active desk in the display, as this
         //  method does.
-        // Case 1: Freeform task is changed in Desktop Mode.
-        if (isFreeformTask(taskInfo)) {
-            if (taskInfo.isVisible) {
-                desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+        // Case 1: When the task change is from a task in the desktop repository which is now
+        // fullscreen,
+        // remove the task from the desktop repository since it is no longer a freeform task.
+        if (!isFreeformTask(taskInfo)) {
+            if (desktopRepository.isActiveTask(taskInfo.taskId)) {
+                desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
             }
-            desktopRepository.updateTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
-        } else {
-            // Case 2: Freeform task is changed outside Desktop Mode.
-            desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
+        } else { // Task change is a freeform task
+            if (!desktopRepository.isActiveTask(taskInfo.taskId)) {
+                // Case 2: When the task change is a freeform visible task, but the task is not
+                // yet active in the desktop repository, adds task to desktop repository.
+                desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+            } else {
+                // Case 3: When the task change is a freeform task which already exists as an active
+                // task in the desktop repository, updates the task state.
+                desktopRepository.updateTask(
+                    taskInfo.displayId,
+                    taskInfo.taskId,
+                    taskInfo.isVisible,
+                )
+            }
         }
     }
 
@@ -82,14 +92,22 @@
         logD("onTaskMovingToFront for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
         val desktopRepository: DesktopRepository =
             desktopUserRepositories.getProfile(taskInfo.userId)
-        if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
-        if (!isFreeformTask(taskInfo)) {
+        // When the task change is from a task in the desktop repository which is now fullscreen,
+        // remove the task from the desktop repository since it is no longer a freeform task.
+        if (!isFreeformTask(taskInfo) && desktopRepository.isActiveTask(taskInfo.taskId)) {
             desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
         }
-        desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+        if (isFreeformTask(taskInfo)) {
+            // If the task is already active in the repository, then it only moves the task to the
+            // front.
+            desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+        }
     }
 
     override fun onTaskMovingToBack(taskInfo: RunningTaskInfo) {
+        val desktopRepository: DesktopRepository =
+            desktopUserRepositories.getProfile(taskInfo.userId)
+        if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
         logD("onTaskMovingToBack for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
         // TODO: b/367268953 - Connect this with DesktopRepository.
     }
@@ -101,7 +119,7 @@
         if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
         // TODO: b/370038902 - Handle Activity#finishAndRemoveTask.
         if (
-            !DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue() ||
+            !DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue ||
                 desktopRepository.isClosingTask(taskInfo.taskId)
         ) {
             // A task that's vanishing should be removed:
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/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 1ce24f7..d16c578 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -345,7 +345,6 @@
             return;
         }
 
-        mPipTouchHandler.updateMinMaxSize(mPipBoundsState.getAspectRatio());
         mPipMenuController.hideMenu();
 
         if (mPipTransitionState.isInFixedRotation()) {
@@ -366,6 +365,8 @@
             mPipBoundsState.setBounds(toBounds);
         }
         t.setBounds(mPipTransitionState.getPipTaskToken(), mPipBoundsState.getBounds());
+        // Update the size spec in PipBoundsState afterwards.
+        mPipBoundsState.updateMinMaxSize(mPipBoundsState.getAspectRatio());
     }
 
     private void setDisplayLayout(DisplayLayout layout) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
index af1e98a..d53365a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
@@ -81,8 +81,6 @@
     private final PointF mDownSecondPoint = new PointF();
     private final PointF mLastPoint = new PointF();
     private final PointF mLastSecondPoint = new PointF();
-    private final Point mMaxSize = new Point();
-    private final Point mMinSize = new Point();
     private final Rect mLastResizeBounds = new Rect();
     private final Rect mUserResizeBounds = new Rect();
     private final Rect mDownBounds = new Rect();
@@ -95,7 +93,6 @@
     private boolean mIsEnabled;
     private boolean mEnablePinchResize;
     private boolean mEnableDragCornerResize;
-    private boolean mIsSysUiStateValid;
     private boolean mThresholdCrossed;
     private boolean mOngoingPinchToResize = false;
     private boolean mWaitingForBoundsChangeTransition = false;
@@ -152,7 +149,6 @@
     }
 
     void init() {
-        mContext.getDisplay().getRealSize(mMaxSize);
         reloadResources();
 
         final Resources res = mContext.getResources();
@@ -163,15 +159,6 @@
         reloadResources();
     }
 
-    /**
-     * Called when SysUI state changed.
-     *
-     * @param isSysUiStateValid Is SysUI valid or not.
-     */
-    public void onSystemUiStateChanged(boolean isSysUiStateValid) {
-        mIsSysUiStateValid = isSysUiStateValid;
-    }
-
     private void reloadResources() {
         mPipDragToResizeHandler.reloadResources();
         mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
@@ -287,13 +274,15 @@
                 }
             }
 
+            final Point minSize = mPipBoundsState.getMinSize();
+            final Point maxSize = mPipBoundsState.getMaxSize();
             if (mOngoingPinchToResize) {
                 mPipPinchToResizeHandler.onPinchResize(mv, mDownPoint, mDownSecondPoint,
                         mDownBounds, mLastPoint, mLastSecondPoint, mLastResizeBounds, mTouchSlop,
-                        mMinSize, mMaxSize);
+                        minSize, maxSize);
             } else if (mEnableDragCornerResize) {
                 mPipDragToResizeHandler.onDragCornerResize(mv, mLastResizeBounds, mDownPoint,
-                        mDownBounds, mMinSize, mMaxSize, mTouchSlop);
+                        mDownBounds, minSize, maxSize, mTouchSlop);
             }
         }
     }
@@ -327,7 +316,7 @@
                 if (mEnablePinchResize && ev.getPointerCount() == 2) {
                     mPipPinchToResizeHandler.onPinchResize(ev, mDownPoint, mDownSecondPoint,
                             mDownBounds, mLastPoint, mLastSecondPoint, mLastResizeBounds,
-                            mTouchSlop, mMinSize, mMaxSize);
+                            mTouchSlop, mPipBoundsState.getMinSize(), mPipBoundsState.getMaxSize());
                     mOngoingPinchToResize = mAllowGesture;
                     return mAllowGesture;
                 }
@@ -407,16 +396,19 @@
             return;
         }
 
+        final Point minSize = mPipBoundsState.getMinSize();
+        final Point maxSize = mPipBoundsState.getMaxSize();
+
         // If user resize is pretty close to max size, just auto resize to max.
-        if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x
-                || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) {
-            resizeRectAboutCenter(mLastResizeBounds, mMaxSize.x, mMaxSize.y);
+        if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * maxSize.x
+                || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * maxSize.y) {
+            resizeRectAboutCenter(mLastResizeBounds, maxSize.x, maxSize.y);
         }
 
         // If user resize is smaller than min size, auto resize to min
-        if (mLastResizeBounds.width() < mMinSize.x
-                || mLastResizeBounds.height() < mMinSize.y) {
-            resizeRectAboutCenter(mLastResizeBounds, mMinSize.x, mMinSize.y);
+        if (mLastResizeBounds.width() < minSize.x
+                || mLastResizeBounds.height() < minSize.y) {
+            resizeRectAboutCenter(mLastResizeBounds, minSize.x, minSize.y);
         }
 
         // get the current movement bounds
@@ -472,15 +464,6 @@
         mInputMonitor.pilferPointers();
     }
 
-
-    void updateMaxSize(int maxX, int maxY) {
-        mMaxSize.set(maxX, maxY);
-    }
-
-    void updateMinSize(int minX, int minY) {
-        mMinSize.set(minX, minY);
-    }
-
     void setOhmOffset(int offset) {
         mOhmOffset = offset;
     }
@@ -568,8 +551,6 @@
         pw.println(innerPrefix + "mEnableDragCornerResize=" + mEnableDragCornerResize);
         pw.println(innerPrefix + "mThresholdCrossed=" + mThresholdCrossed);
         pw.println(innerPrefix + "mOhmOffset=" + mOhmOffset);
-        pw.println(innerPrefix + "mMinSize=" + mMinSize);
-        pw.println(innerPrefix + "mMaxSize=" + mMaxSize);
     }
 
     class PipResizeInputEventReceiver extends BatchedInputEventReceiver {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index 72346b3..6fdfeca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -230,10 +230,7 @@
                 pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState, pipUiEventLogger,
                 menuController, this::getMovementBounds, mPipDisplayLayoutState, pipDesktopState,
                 mainExecutor, mPipPerfHintController);
-        mPipBoundsState.addOnAspectRatioChangedCallback(aspectRatio -> {
-            updateMinMaxSize(aspectRatio);
-            onAspectRatioChanged();
-        });
+        mPipBoundsState.addOnAspectRatioChangedCallback(aspectRatio -> onAspectRatioChanged());
 
         mMoveOnShelVisibilityChanged = () -> {
             if (mIsImeShowing && mImeHeight > mShelfHeight) {
@@ -418,15 +415,6 @@
         mMainExecutor.executeDelayed(mMoveOnShelVisibilityChanged, PIP_KEEP_CLEAR_AREAS_DELAY);
     }
 
-    /**
-     * Called when SysUI state changed.
-     *
-     * @param isSysUiStateValid Is SysUI valid or not.
-     */
-    public void onSystemUiStateChanged(boolean isSysUiStateValid) {
-        mPipResizeGestureHandler.onSystemUiStateChanged(isSysUiStateValid);
-    }
-
     void adjustBoundsForRotation(Rect outBounds, Rect curBounds, Rect insetBounds) {
         final Rect toMovementBounds = new Rect();
         mPipBoundsAlgorithm.getMovementBounds(outBounds, insetBounds, toMovementBounds, 0);
@@ -480,8 +468,6 @@
                 mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds,
                 bottomOffset);
 
-        updatePipSizeConstraints(normalBounds, aspectRatio);
-
         // The extra offset does not really affect the movement bounds, but are applied based on the
         // current state (ime showing, or shelf offset) when we need to actually shift
         int extraOffset = Math.max(
@@ -507,35 +493,6 @@
     }
 
     /**
-     * Update the values for min/max allowed size of picture in picture window based on the aspect
-     * ratio.
-     * @param aspectRatio aspect ratio to use for the calculation of min/max size
-     */
-    public void updateMinMaxSize(float aspectRatio) {
-        updatePipSizeConstraints(mPipBoundsState.getNormalBounds(),
-                aspectRatio);
-    }
-
-    private void updatePipSizeConstraints(Rect normalBounds,
-            float aspectRatio) {
-        if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
-            updatePinchResizeSizeConstraints(aspectRatio);
-        } else {
-            mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
-            mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
-                    mPipBoundsState.getExpandedBounds().height());
-        }
-    }
-
-    private void updatePinchResizeSizeConstraints(float aspectRatio) {
-        mPipBoundsState.updateMinMaxSize(aspectRatio);
-        mPipResizeGestureHandler.updateMinSize(mPipBoundsState.getMinSize().x,
-                mPipBoundsState.getMinSize().y);
-        mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getMaxSize().x,
-                mPipBoundsState.getMaxSize().y);
-    }
-
-    /**
      * TODO Add appropriate description
      */
     public void onRegistrationChanged(boolean isRegistered) {
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..a3a808d 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;
@@ -2079,8 +2080,8 @@
                             Math.max(topLeftBounds.top, 0);
                     bottomRightBounds.right =
                             Math.min(bottomRightBounds.right, mSplitLayout.getDisplayWidth());
-                    bottomRightBounds.top =
-                            Math.min(bottomRightBounds.top, mSplitLayout.getDisplayHeight());
+                    bottomRightBounds.bottom =
+                            Math.min(bottomRightBounds.bottom, mSplitLayout.getDisplayHeight());
 
                     // TODO (b/349828130): Can change to getState() fully after brief soak time.
                     if (mSplitState.get() != currentSnapPosition) {
@@ -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/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 3652a16..bf58003 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -95,7 +95,6 @@
 import android.util.ArrayMap;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
-import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.window.TransitionInfo;
 import android.window.TransitionMetrics;
@@ -835,9 +834,8 @@
                 a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter, userId);
             }
         } else if (changeMode == TRANSIT_CHANGE) {
-            // In the absence of a specific adapter, we just want to keep everything stationary.
-            a = new AlphaAnimation(1.f, 1.f);
-            a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION);
+            // Apply end state directly by default.
+            return null;
         } else if (type == TRANSIT_RELAUNCH) {
             a = mTransitionAnimation.createRelaunchAnimation(endBounds, mInsets, endBounds);
         } else if (overrideType == ANIM_CUSTOM
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
index d054de4..cc37c44 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
@@ -26,7 +26,6 @@
 import android.platform.test.annotations.EnableFlags
 import android.provider.Settings
 import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
-import android.testing.AndroidTestingRunner
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.IWindowManager
 import android.view.WindowManager.TRANSIT_CHANGE
@@ -43,6 +42,8 @@
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
 import com.android.wm.shell.transition.Transitions
 import com.google.common.truth.Truth.assertThat
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -62,7 +63,7 @@
  * Usage: atest WMShellUnitTests:DesktopDisplayModeControllerTest
  */
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(TestParameterInjector::class)
 class DesktopDisplayModeControllerTest : ShellTestCase() {
     private val transitions = mock<Transitions>()
     private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
@@ -143,41 +144,24 @@
 
     @Test
     @DisableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
-    fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay_flagDisabled() {
+    fun displayWindowingModeSwitchOnDisplayConnected_flagDisabled(
+        @TestParameter param: ModeSwitchTestCase
+    ) {
         testDisplayWindowingModeSwitch(
-            defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
-            extendedDisplayEnabled = true,
+            param.defaultWindowingMode,
+            param.extendedDisplayEnabled,
+            // When the flag is disabled, never switch.
             expectToSwitch = false,
         )
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
-    fun displayWindowingModeSwitchOnDisplayConnected_extendedDisplayDisabled() {
+    fun displayWindowingModeSwitchOnDisplayConnected(@TestParameter param: ModeSwitchTestCase) {
         testDisplayWindowingModeSwitch(
-            defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
-            extendedDisplayEnabled = false,
-            expectToSwitch = false,
-        )
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
-    fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay() {
-        testDisplayWindowingModeSwitch(
-            defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
-            extendedDisplayEnabled = true,
-            expectToSwitch = true,
-        )
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
-    fun displayWindowingModeSwitchOnDisplayConnected_freeformDisplay() {
-        testDisplayWindowingModeSwitch(
-            defaultWindowingMode = WINDOWING_MODE_FREEFORM,
-            extendedDisplayEnabled = true,
-            expectToSwitch = false,
+            param.defaultWindowingMode,
+            param.extendedDisplayEnabled,
+            param.expectToSwitchByDefault,
         )
     }
 
@@ -249,7 +233,34 @@
         }
     }
 
-    private companion object {
+    companion object {
         const val EXTERNAL_DISPLAY_ID = 100
+
+        enum class ModeSwitchTestCase(
+            val defaultWindowingMode: Int,
+            val extendedDisplayEnabled: Boolean,
+            val expectToSwitchByDefault: Boolean,
+        ) {
+            FULLSCREEN_DISPLAY(
+                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+                extendedDisplayEnabled = true,
+                expectToSwitchByDefault = true,
+            ),
+            FULLSCREEN_DISPLAY_MIRRORING(
+                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+                extendedDisplayEnabled = false,
+                expectToSwitchByDefault = false,
+            ),
+            FREEFORM_DISPLAY(
+                defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+                extendedDisplayEnabled = true,
+                expectToSwitchByDefault = false,
+            ),
+            FREEFORM_DISPLAY_MIRRORING(
+                defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+                extendedDisplayEnabled = false,
+                expectToSwitchByDefault = false,
+            ),
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
index 50590f0..6b0ee5b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
@@ -56,7 +56,7 @@
     }
 
     @Test
-    fun onTaskOpening_fullscreenTask_notActiveDesktopTask_noop() {
+    fun onTaskOpening_fullscreenTask_nonActiveDesktopTask_noop() {
         val task = createFullscreenTask().apply { isVisible = true }
         whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
 
@@ -68,7 +68,7 @@
     }
 
     @Test
-    fun onTaskOpening_freeformTask_activeDesktopTask_removesTaskFromRepo() {
+    fun onTaskOpening_fullscreenTask_taskIsActiveInDesktopRepo_removesTaskFromDesktopRepo() {
         val task = createFullscreenTask().apply { isVisible = true }
         whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
 
@@ -78,8 +78,19 @@
     }
 
     @Test
-    fun onTaskOpening_freeformTask_visibleDesktopTask_addsTaskToRepository() {
+    fun onTaskOpening_freeformTask_activeInDesktopRepository_noop() {
         val task = createFreeformTask().apply { isVisible = true }
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+
+        desktopTaskChangeListener.onTaskOpening(task)
+
+        verify(desktopUserRepositories.current, never())
+            .addTask(task.displayId, task.taskId, task.isVisible)
+    }
+
+    @Test
+    fun onTaskOpening_freeformTask_notActiveInDesktopRepo_addsTaskToRepository() {
+        val task = createFreeformTask().apply { isVisible = false }
         whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
 
         desktopTaskChangeListener.onTaskOpening(task)
@@ -88,17 +99,7 @@
     }
 
     @Test
-    fun onTaskOpening_freeformTask_nonVisibleDesktopTask_addsTaskToRepository() {
-        val task = createFreeformTask().apply { isVisible = false }
-        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
-
-        desktopTaskChangeListener.onTaskOpening(task)
-
-        verify(desktopUserRepositories.current).addTask(task.displayId, task.taskId, task.isVisible)
-    }
-
-    @Test
-    fun onTaskChanging_freeformTaskOutsideDesktop_removesTaskFromRepo() {
+    fun onTaskChanging_fullscreenTask_activeInDesktopRepository_removesTaskFromRepo() {
         val task = createFullscreenTask().apply { isVisible = true }
         whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
 
@@ -108,7 +109,27 @@
     }
 
     @Test
-    fun onTaskChanging_visibleTaskInDesktop_updatesTaskVisibility() {
+    fun onTaskChanging_fullscreenTask_nonActiveInDesktopRepo_noop() {
+        val task = createFullscreenTask().apply { isVisible = true }
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
+
+        desktopTaskChangeListener.onTaskChanging(task)
+
+        verify(desktopUserRepositories.current, never()).removeTask(task.displayId, task.taskId)
+    }
+
+    @Test
+    fun onTaskChanging_freeformTask_nonActiveTaskInDesktopRepo_addsTaskToDesktopRepo() {
+        val task = createFreeformTask().apply { isVisible = true }
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
+
+        desktopTaskChangeListener.onTaskChanging(task)
+
+        verify(desktopUserRepositories.current).addTask(task.displayId, task.taskId, task.isVisible)
+    }
+
+    @Test
+    fun onTaskChanging_freeformTask_activeVisibleTaskInDesktopRepo_updatesTaskVisibility() {
         val task = createFreeformTask().apply { isVisible = true }
         whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
 
@@ -119,7 +140,7 @@
     }
 
     @Test
-    fun onTaskChanging_nonVisibleTask_updatesTaskVisibility() {
+    fun onTaskChanging_freeformTask_activeNonVisibleTask_updatesTaskVisibility() {
         val task = createFreeformTask().apply { isVisible = false }
         whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
 
@@ -130,7 +151,7 @@
     }
 
     @Test
-    fun onTaskMovingToFront_freeformTaskOutsideDesktop_removesTaskFromRepo() {
+    fun onTaskMovingToFront_fullscreenTask_activeTaskInDesktopRepo_removesTaskFromRepo() {
         val task = createFullscreenTask().apply { isVisible = true }
         whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
 
@@ -140,9 +161,18 @@
     }
 
     @Test
-    fun onTaskMovingToFront_freeformTaskOutsideDesktop_addsTaskToRepo() {
+    fun onTaskMovingToFront_fullscreenTask_nonActiveTaskInDesktopRepo_noop() {
         val task = createFullscreenTask().apply { isVisible = true }
-        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
+
+        desktopTaskChangeListener.onTaskMovingToFront(task)
+
+        verify(desktopUserRepositories.current, never()).removeTask(task.displayId, task.taskId)
+    }
+
+    @Test
+    fun onTaskMovingToFront_freeformTask_addsTaskToRepo() {
+        val task = createFreeformTask().apply { isVisible = true }
 
         desktopTaskChangeListener.onTaskMovingToFront(task)
 
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/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index fb89973..66de8c7 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -27,8 +27,6 @@
 import android.telephony.emergency.EmergencyNumber;
 import android.util.Log;
 
-import com.android.internal.annotations.KeepForWeakReference;
-
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -94,7 +92,6 @@
 
     // The internal implementation of TelephonyManager uses WeakReference so we have to keep a
     // reference here.
-    @KeepForWeakReference
     private final EmergencyCallListener mEmergencyCallListener = new EmergencyCallListener();
 
     private final EmergencyCallCallback mEmergencyCallCallback;
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/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 9bb31d0..4e86eac 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1223,8 +1223,14 @@
         private static final String TAG = "CodecCapabilities";
 
         // NEW-STYLE CAPABILITIES
+        // Capabilities for an audio codec.
+        @Nullable
         private AudioCapabilities mAudioCaps;
+        // Capabilities for a video codec.
+        @Nullable
         private VideoCapabilities mVideoCaps;
+        // Capabilities specific to an encoder (vs. a decoder).
+        @Nullable
         private EncoderCapabilities mEncoderCaps;
         private MediaFormat mDefaultFormat;
 
@@ -1262,6 +1268,7 @@
         /**
          * Returns the audio capabilities or {@code null} if this is not an audio codec.
          */
+        @Nullable
         public AudioCapabilities getAudioCapabilities() {
             return mAudioCaps;
         }
@@ -1273,6 +1280,7 @@
         /**
          * Returns the encoding capabilities or {@code null} if this is not an encoder.
          */
+        @Nullable
         public EncoderCapabilities getEncoderCapabilities() {
             return mEncoderCaps;
         }
@@ -1284,6 +1292,7 @@
         /**
          * Returns the video capabilities or {@code null} if this is not a video codec.
          */
+        @Nullable
         public VideoCapabilities getVideoCapabilities() {
             return mVideoCaps;
         }
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/Utils.java b/media/java/android/media/Utils.java
index d6e27b0..9db527b 100644
--- a/media/java/android/media/Utils.java
+++ b/media/java/android/media/Utils.java
@@ -18,6 +18,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
@@ -61,16 +63,30 @@
  *
  * @hide
  */
+@TestApi
+@SuppressLint({"UnflaggedApi", "StaticUtils"}) // Test API
 public class Utils {
     private static final String TAG = "Utils";
 
+    /** @hide
+     * The vibration uri key parameter
+     */
+    @TestApi
+    @SuppressLint("UnflaggedApi") // Test API
     public static final String VIBRATION_URI_PARAM = "vibration_uri";
 
+    /** @hide
+     * Indicates the synchronized vibration
+     */
+    @TestApi
+    @SuppressLint("UnflaggedApi") // Test API
     public static final String SYNCHRONIZED_VIBRATION = "synchronized";
 
     /**
      * Sorts distinct (non-intersecting) range array in ascending order.
      * @throws java.lang.IllegalArgumentException if ranges are not distinct
+     *
+     * @hide
      */
     public static <T extends Comparable<? super T>> void sortDistinctRanges(Range<T>[] ranges) {
         Arrays.sort(ranges, new Comparator<Range<T>>() {
@@ -92,6 +108,8 @@
      * @param one a sorted set of non-intersecting ranges in ascending order
      * @param another another sorted set of non-intersecting ranges in ascending order
      * @return the intersection of the two sets, sorted in ascending order
+     *
+     * @hide
      */
     public static <T extends Comparable<? super T>>
             Range<T>[] intersectSortedDistinctRanges(Range<T>[] one, Range<T>[] another) {
@@ -124,6 +142,8 @@
      * @return if the value is in one of the ranges, it returns the index of that range.  Otherwise,
      * the return value is {@code (-1-index)} for the {@code index} of the range that is
      * immediately following {@code value}.
+     *
+     * @hide
      */
     public static <T extends Comparable<? super T>>
             int binarySearchDistinctRanges(Range<T>[] ranges, T value) {
@@ -358,6 +378,8 @@
      * @param fileName desired name for the file.
      * @param mimeType MIME type of the file to create.
      * @return the File object in the storage, or null if an error occurs.
+     *
+     * @hide
      */
     public static File getUniqueExternalFile(Context context, String subdirectory, String fileName,
             String mimeType) {
@@ -676,6 +698,8 @@
      * Must match the implementation of BluetoothUtils.toAnonymizedAddress()
      * @param address MAC address to be anonymized
      * @return anonymized MAC address
+     *
+     * @hide
      */
     public static @Nullable String anonymizeBluetoothAddress(@Nullable String address) {
         if (address == null) {
@@ -693,6 +717,8 @@
      * @param deviceType the internal type of the audio device
      * @param address MAC address to be anonymized
      * @return anonymized MAC address
+     *
+     * @hide
      */
     public static @Nullable String anonymizeBluetoothAddress(
             int deviceType, @Nullable String address) {
@@ -707,6 +733,8 @@
      *
      * @param context the {@link Context}
      * @return {@code true} if the device supports ringtone vibration
+     *
+     * @hide
      */
     public static boolean isRingtoneVibrationSettingsSupported(Context context) {
         final Resources res = context.getResources();
@@ -719,6 +747,8 @@
      *
      * @param ringtoneUri the ringtone Uri
      * @return {@code true} if the Uri has vibration parameter
+     *
+     * @hide
      */
     public static boolean hasVibration(Uri ringtoneUri) {
         if (ringtoneUri == null) {
@@ -734,6 +764,8 @@
      * @param ringtoneUri the ringtone Uri
      * @return parsed {@link Uri} of vibration parameter, {@code null} if the vibration parameter
      * is not found.
+     *
+     * @hide
      */
     public static @Nullable Uri getVibrationUri(Uri ringtoneUri) {
         if (ringtoneUri == null) {
@@ -751,6 +783,8 @@
      *
      * @param vibrator the vibrator to resolve the vibration file
      * @param vibrationUri the vibration file Uri to represent a vibration
+     *
+     * @hide
      */
     @SuppressWarnings("FlaggedApi") // VibrationXmlParser is available internally as hidden APIs.
     public static VibrationEffect parseVibrationEffect(Vibrator vibrator, Uri vibrationUri) {
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/native/webview/TEST_MAPPING b/native/webview/TEST_MAPPING
index 3858059..c9b5476 100644
--- a/native/webview/TEST_MAPPING
+++ b/native/webview/TEST_MAPPING
@@ -17,15 +17,6 @@
           "exclude-annotation": "android.test.FlakyTest"
         }
       ]
-    },
-    {
-      "name": "GtsWebViewHostTestCases",
-      "keywords": ["internal"],
-      "options": [
-        {
-          "exclude-annotation": "android.test.FlakyTest"
-        }
-      ]
     }
   ]
 }
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/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml
index 6221659..3db0ac6 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml
@@ -41,6 +41,7 @@
                 android:id="@+id/action_bar"
                 android:layout_width="match_parent"
                 android:layout_height="?attr/actionBarSize"
+                android:layout_marginStart="@dimen/settingslib_expressive_space_extrasmall4"
                 android:theme="?android:attr/actionBarTheme"
                 android:transitionName="shared_element_view"
                 app:layout_collapseMode="pin"/>
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
index 04df308..7348920 100644
--- a/packages/SettingsLib/OWNERS
+++ b/packages/SettingsLib/OWNERS
@@ -8,6 +8,7 @@
 evanlaird@google.com
 jiannan@google.com
 juliacr@google.com
+millchen@google.com
 ykhung@google.com
 
 # Exempt resource files (because they are in a flat directory and too hard to manage via OWNERS)
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/NormalPaddingMixin.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/NormalPaddingMixin.kt
new file mode 100644
index 0000000..5035542
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/NormalPaddingMixin.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget
+
+/**
+ * A base interface to indicate that a Preference should have normal paddings.
+ *
+ * Preferences implementing this interface will be treated as has normal paddings both inside and
+ * outside.
+ */
+interface NormalPaddingMixin
\ No newline at end of file
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/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
index a04fce7..2672787 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
@@ -177,14 +177,32 @@
         val v = holder.itemView
         // Update padding
         if (SettingsThemeHelper.isExpressiveTheme(context)) {
-            val paddingStart = if (backgroundRes == 0) mNormalPaddingStart else mGroupPaddingStart
-            val paddingEnd = if (backgroundRes == 0) mNormalPaddingEnd else mGroupPaddingEnd
+            val (paddingStart, paddingEnd) = getStartEndPadding(position, backgroundRes)
             v.setPaddingRelative(paddingStart, v.paddingTop, paddingEnd, v.paddingBottom)
+            v.clipToOutline = backgroundRes != 0
         }
         // Update background
         v.setBackgroundResource(backgroundRes)
     }
 
+    private fun getStartEndPadding(position: Int, backgroundRes: Int): Pair<Int, Int> {
+        val item = getItem(position)
+        return when {
+            // This item handles edge to edge itself
+            item is NormalPaddingMixin && item is GroupSectionDividerMixin -> 0 to 0
+
+            // According to mappingPreferenceGroup(), backgroundRes == 0 means this item is
+            // GroupSectionDividerMixin or PreferenceCategory, which is design to have normal
+            // padding.
+            // NormalPaddingMixin items are also designed to have normal padding.
+            backgroundRes == 0 || item is NormalPaddingMixin ->
+                mNormalPaddingStart to mNormalPaddingEnd
+
+            // Other items are suppose to have group padding.
+            else -> mGroupPaddingStart to mGroupPaddingEnd
+        }
+    }
+
     @DrawableRes
     protected fun getRoundCornerDrawableRes(position: Int, isSelected: Boolean): Int {
         return getRoundCornerDrawableRes(position, isSelected, false)
diff --git a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
index 4315238..fe8e8b6 100644
--- a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
+++ b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
@@ -22,6 +22,7 @@
 import android.content.res.TypedArray;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -72,6 +73,7 @@
     private int mSliderIncrement;
     private boolean mAdjustable;
     private boolean mTrackingTouch;
+    private CharSequence mSliderContentDescription;
 
     /**
      * Listener reacting to the user pressing DPAD left/right keys if {@code
@@ -143,6 +145,7 @@
             @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setLayoutResource(R.layout.settingslib_expressive_preference_slider);
+        setSelectable(false);
 
         TypedArray a = context.obtainStyledAttributes(
                 attrs, androidx.preference.R.styleable.SeekBarPreference, defStyleAttr,
@@ -265,6 +268,14 @@
         } else {
             mSliderIncrement = (int) (mSlider.getStepSize());
         }
+        final CharSequence title = getTitle();
+        if (!TextUtils.isEmpty(mSliderContentDescription)) {
+            mSlider.setContentDescription(mSliderContentDescription);
+        } else if (!TextUtils.isEmpty(title)) {
+            mSlider.setContentDescription(title);
+        } else {
+            mSlider.setContentDescription(null);
+        }
         mSlider.setValueFrom(mMin);
         mSlider.setValueTo(mMax);
         mSlider.setValue(mSliderValue);
@@ -273,6 +284,8 @@
         mSlider.clearOnChangeListeners();
         mSlider.addOnChangeListener(mChangeListener);
         mSlider.setEnabled(isEnabled());
+        mSlider.setFocusable(isSelectable());
+        mSlider.setClickable(isSelectable());
 
         // Set up slider color
         mSlider.setTrackActiveTintList(mTrackActiveColor);
@@ -471,6 +484,19 @@
         setValueInternal(sliderValue, true);
     }
 
+
+    /**
+     * Sets the content description of the {@link Slider}.
+     *
+     * @param contentDescription The content description of the {@link Slider}
+     */
+    public void setSliderContentDescription(@Nullable CharSequence contentDescription) {
+        mSliderContentDescription = contentDescription;
+        if (mSlider != null) {
+            mSlider.setContentDescription(contentDescription);
+        }
+    }
+
     @Override
     protected void onSetInitialValue(@Nullable Object defaultValue) {
         if (defaultValue == null) {
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/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 2c6d09a..2d03e2b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -23,7 +23,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.IntRect
@@ -35,7 +34,6 @@
 import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
 import com.android.systemui.communal.ui.compose.section.CommunalLockSection
 import com.android.systemui.communal.ui.compose.section.CommunalPopupSection
-import com.android.systemui.communal.ui.compose.section.CommunalToDreamButtonSection
 import com.android.systemui.communal.ui.compose.section.HubOnboardingSection
 import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
@@ -61,7 +59,6 @@
     private val ambientStatusBarSection: AmbientStatusBarSection,
     private val communalPopupSection: CommunalPopupSection,
     private val widgetSection: CommunalAppWidgetSection,
-    private val communalToDreamButtonSection: CommunalToDreamButtonSection,
     private val hubOnboardingSection: HubOnboardingSection,
 ) {
 
@@ -103,13 +100,11 @@
                             Modifier.element(Communal.Elements.IndicationArea).fillMaxWidth()
                         )
                     }
-                    with(communalToDreamButtonSection) { Button() }
                 },
             ) { measurables, constraints ->
                 val communalGridMeasurable = measurables[0]
                 val lockIconMeasurable = measurables[1]
                 val bottomAreaMeasurable = measurables[2]
-                val screensaverButtonMeasurable: Measurable? = measurables.getOrNull(3)
 
                 val noMinConstraints = constraints.copy(minWidth = 0, minHeight = 0)
 
@@ -152,9 +147,6 @@
 
                 val bottomAreaPlaceable = bottomAreaMeasurable.measure(noMinConstraints)
 
-                val screensaverButtonPlaceable =
-                    screensaverButtonMeasurable?.measure(noMinConstraints)
-
                 val communalGridPlaceable =
                     communalGridMeasurable.measure(
                         noMinConstraints.copy(maxHeight = lockIconBounds.top)
@@ -166,26 +158,12 @@
 
                     val bottomAreaTop = constraints.maxHeight - bottomAreaPlaceable.height
                     bottomAreaPlaceable.place(x = 0, y = bottomAreaTop)
-
-                    val screensaverButtonPaddingInt = screensaverButtonPadding.roundToPx()
-                    screensaverButtonPlaceable?.place(
-                        x =
-                            constraints.maxWidth -
-                                screensaverButtonPaddingInt -
-                                screensaverButtonPlaceable.width,
-                        y =
-                            constraints.maxHeight -
-                                screensaverButtonPaddingInt -
-                                screensaverButtonPlaceable.height,
-                    )
                 }
             }
         }
     }
 
     companion object {
-        private val screensaverButtonPadding: Dp = 24.dp
-
         // TODO(b/382739998): Remove these hardcoded values once lock icon size and bottom area
         // position are sorted.
         private val lockIconSize: Dp = 54.dp
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
deleted file mode 100644
index acaf43a..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.ui.compose.section
-
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.core.MutableTransitionState
-import androidx.compose.animation.expandVertically
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.shrinkVertically
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.layout.widthIn
-import androidx.compose.foundation.shape.CornerSize
-import androidx.compose.material3.IconButtonDefaults
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.CornerRadius
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.RoundRect
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.geometry.toRect
-import androidx.compose.ui.graphics.Outline
-import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
-import com.android.compose.PlatformIconButton
-import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
-import com.android.systemui.communal.ui.compose.extensions.observeTaps
-import com.android.systemui.communal.ui.viewmodel.CommunalToDreamButtonViewModel
-import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.res.R
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.delay
-
-class CommunalToDreamButtonSection
-@Inject
-constructor(
-    private val communalSettingsInteractor: CommunalSettingsInteractor,
-    private val viewModelFactory: CommunalToDreamButtonViewModel.Factory,
-) {
-    @Composable
-    fun Button() {
-        if (!communalSettingsInteractor.isV2FlagEnabled()) {
-            return
-        }
-
-        val viewModel =
-            rememberViewModel("CommunalToDreamButtonSection") { viewModelFactory.create() }
-
-        if (!viewModel.shouldShowDreamButtonOnHub) {
-            return
-        }
-
-        val buttonSize = dimensionResource(R.dimen.communal_to_dream_button_size)
-
-        if (viewModel.shouldShowTooltip) {
-            val tooltipVisibleState = remember { MutableTransitionState(false) }
-
-            Column(
-                modifier =
-                    Modifier.widthIn(max = tooltipMaxWidth).pointerInput(Unit) {
-                        observeTaps {
-                            if (tooltipVisibleState.isCurrentlyVisible()) {
-                                tooltipVisibleState.targetState = false
-                            }
-                        }
-                    }
-            ) {
-                var waitingToShowTooltip by remember { mutableStateOf(true) }
-
-                LaunchedEffect(tooltipVisibleState.targetState) {
-                    delay(3.seconds)
-                    tooltipVisibleState.targetState = true
-                    waitingToShowTooltip = false
-                }
-
-                // This LaunchedEffect is used to wait for the tooltip dismiss animation to
-                // complete before setting the tooltip dismissed. Otherwise, the composable would
-                // be removed before the animation can start.
-                LaunchedEffect(
-                    tooltipVisibleState.currentState,
-                    tooltipVisibleState.isIdle,
-                    waitingToShowTooltip,
-                ) {
-                    if (
-                        !waitingToShowTooltip &&
-                            !tooltipVisibleState.currentState &&
-                            tooltipVisibleState.isIdle
-                    ) {
-                        viewModel.setDreamButtonTooltipDismissed()
-                    }
-                }
-
-                AnimatedVisibility(
-                    visibleState = tooltipVisibleState,
-                    enter = fadeIn() + expandVertically(expandFrom = Alignment.Bottom),
-                    exit = fadeOut() + shrinkVertically(shrinkTowards = Alignment.Bottom),
-                ) {
-                    Tooltip(
-                        pointerOffsetDp = buttonSize.div(2),
-                        text = stringResource(R.string.glanceable_hub_to_dream_button_tooltip),
-                    )
-                }
-
-                GoToDreamButton(
-                    modifier = Modifier.width(buttonSize).height(buttonSize).align(Alignment.End)
-                ) {
-                    viewModel.onShowDreamButtonTap()
-                }
-            }
-        } else {
-            GoToDreamButton(modifier = Modifier.width(buttonSize).height(buttonSize)) {
-                viewModel.onShowDreamButtonTap()
-            }
-        }
-    }
-
-    private fun MutableTransitionState<Boolean>.isCurrentlyVisible() = currentState && isIdle
-
-    companion object {
-        private val tooltipMaxWidth = 350.dp
-    }
-}
-
-@Composable
-private fun GoToDreamButton(modifier: Modifier, onClick: () -> Unit) {
-    PlatformIconButton(
-        modifier = modifier,
-        onClick = onClick,
-        iconResource = R.drawable.ic_screensaver_auto,
-        contentDescription = stringResource(R.string.accessibility_glanceable_hub_to_dream_button),
-        colors =
-            IconButtonDefaults.filledIconButtonColors(
-                contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
-                containerColor = MaterialTheme.colorScheme.primaryContainer,
-            ),
-    )
-}
-
-@Composable
-private fun Tooltip(pointerOffsetDp: Dp, text: String) {
-    Surface(
-        color = MaterialTheme.colorScheme.surface,
-        shape = TooltipShape(pointerSizeDp = 12.dp, pointerOffsetDp = pointerOffsetDp),
-    ) {
-        Text(
-            modifier = Modifier.padding(start = 32.dp, top = 16.dp, end = 32.dp, bottom = 32.dp),
-            color = MaterialTheme.colorScheme.onSurface,
-            text = text,
-        )
-    }
-
-    Spacer(modifier = Modifier.height(4.dp))
-}
-
-private class TooltipShape(private val pointerSizeDp: Dp, private val pointerOffsetDp: Dp) : Shape {
-
-    override fun createOutline(
-        size: Size,
-        layoutDirection: LayoutDirection,
-        density: Density,
-    ): Outline {
-
-        val pointerSizePx = with(density) { pointerSizeDp.toPx() }
-        val pointerOffsetPx = with(density) { pointerOffsetDp.toPx() }
-        val cornerRadius = CornerRadius(CornerSize(16.dp).toPx(size, density))
-        val bubbleSize = size.copy(height = size.height - pointerSizePx)
-
-        val path =
-            Path().apply {
-                addRoundRect(
-                    RoundRect(
-                        rect = bubbleSize.toRect(),
-                        topLeft = cornerRadius,
-                        topRight = cornerRadius,
-                        bottomRight = cornerRadius,
-                        bottomLeft = cornerRadius,
-                    )
-                )
-                addPath(
-                    Path().apply {
-                        moveTo(0f, 0f)
-                        lineTo(pointerSizePx / 2f, pointerSizePx)
-                        lineTo(pointerSizePx, 0f)
-                        close()
-                    },
-                    offset =
-                        Offset(
-                            x = bubbleSize.width - pointerOffsetPx - pointerSizePx / 2f,
-                            y = bubbleSize.height,
-                        ),
-                )
-            }
-
-        return Outline.Generic(path)
-    }
-}
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/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
deleted file mode 100644
index 2f3073e..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.ui.viewmodel
-
-import android.content.pm.UserInfo
-import android.platform.test.annotations.EnableFlags
-import android.provider.Settings
-import android.service.dream.dreamManager
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.internal.logging.uiEventLoggerFake
-import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
-import com.android.systemui.communal.domain.interactor.HubOnboardingInteractorTest.Companion.MAIN_USER
-import com.android.systemui.communal.shared.log.CommunalUiEvent
-import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
-import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.kosmos.runCurrent
-import com.android.systemui.kosmos.runTest
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.plugins.activityStarter
-import com.android.systemui.settings.fakeUserTracker
-import com.android.systemui.statusbar.policy.batteryController
-import com.android.systemui.statusbar.policy.fake
-import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.settings.fakeSettings
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mockito.verify
-import org.mockito.kotlin.any
-
-@SmallTest
-@EnableFlags(FLAG_GLANCEABLE_HUB_V2)
-@RunWith(AndroidJUnit4::class)
-class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val underTest: CommunalToDreamButtonViewModel by lazy {
-        kosmos.communalToDreamButtonViewModel
-    }
-
-    @Before
-    fun setUp() {
-        kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
-        underTest.activateIn(testScope)
-    }
-
-    @Test
-    fun shouldShowDreamButtonOnHub_trueWhenPluggedIn() =
-        with(kosmos) {
-            runTest {
-                batteryController.fake._isPluggedIn = true
-                runCurrent()
-
-                assertThat(underTest.shouldShowDreamButtonOnHub).isTrue()
-            }
-        }
-
-    @Test
-    fun shouldShowDreamButtonOnHub_falseWhenNotPluggedIn() =
-        with(kosmos) {
-            runTest {
-                batteryController.fake._isPluggedIn = false
-
-                assertThat(underTest.shouldShowDreamButtonOnHub).isFalse()
-            }
-        }
-
-    @Test
-    fun onShowDreamButtonTap_dreamsEnabled_startsDream() =
-        with(kosmos) {
-            runTest {
-                val currentUser = fakeUserRepository.asMainUser()
-                kosmos.fakeSettings.putIntForUser(
-                    Settings.Secure.SCREENSAVER_ENABLED,
-                    1,
-                    currentUser.id,
-                )
-                runCurrent()
-
-                underTest.onShowDreamButtonTap()
-                runCurrent()
-
-                verify(dreamManager).startDream()
-            }
-        }
-
-    @Test
-    fun onShowDreamButtonTap_dreamsDisabled_startsActivity() =
-        with(kosmos) {
-            runTest {
-                val currentUser = fakeUserRepository.asMainUser()
-                kosmos.fakeSettings.putIntForUser(
-                    Settings.Secure.SCREENSAVER_ENABLED,
-                    0,
-                    currentUser.id,
-                )
-                runCurrent()
-
-                underTest.onShowDreamButtonTap()
-                runCurrent()
-
-                verify(activityStarter).postStartActivityDismissingKeyguard(any(), anyInt())
-            }
-        }
-
-    @Test
-    fun shouldShowDreamButtonTooltip_trueWhenNotDismissedAndHubOnboardingDismissed() =
-        kosmos.runTest {
-            setSelectedUser(MAIN_USER)
-            fakeCommunalPrefsRepository.setHubOnboardingDismissed(MAIN_USER)
-            runCurrent()
-
-            assertThat(underTest.shouldShowTooltip).isTrue()
-        }
-
-    @Test
-    fun shouldShowDreamButtonTooltip_falseWhenNotDismissedAndHubOnboardingNotDismissed() =
-        kosmos.runTest {
-            runCurrent()
-            assertThat(underTest.shouldShowTooltip).isFalse()
-        }
-
-    @Test
-    fun shouldShowDreamButtonTooltip_falseWhenDismissed() =
-        kosmos.runTest {
-            setSelectedUser(MAIN_USER)
-            fakeCommunalPrefsRepository.setDreamButtonTooltipDismissed(MAIN_USER)
-            runCurrent()
-
-            assertThat(underTest.shouldShowTooltip).isFalse()
-        }
-
-    @Test
-    fun onShowDreamButtonTap_eventLogged() =
-        with(kosmos) {
-            runTest {
-                underTest.onShowDreamButtonTap()
-                runCurrent()
-
-                assertThat(uiEventLoggerFake[0].eventId)
-                    .isEqualTo(CommunalUiEvent.COMMUNAL_HUB_SHOW_DREAM_BUTTON_TAP.id)
-            }
-        }
-
-    private suspend fun setSelectedUser(user: UserInfo) {
-        with(kosmos.fakeUserRepository) {
-            setUserInfos(listOf(user))
-            setSelectedUserInfo(user)
-        }
-        kosmos.fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
-    }
-}
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/PhysicsPropertyAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt
index 56cd72e..0bb4737 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt
@@ -15,19 +15,22 @@
  */
 package com.android.systemui.statusbar.notification
 
+import android.animation.AnimatorTestRule
 import android.util.FloatProperty
 import android.util.Property
 import android.view.View
-import androidx.dynamicanimation.animation.DynamicAnimation
+
 import androidx.test.annotation.UiThreadTest
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.internal.dynamicanimation.animation.DynamicAnimation
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.notification.stack.AnimationProperties
 import com.android.systemui.statusbar.notification.stack.ViewState
 import org.junit.Assert
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito
@@ -50,6 +53,8 @@
                 return _value
             }
         }
+    @get:Rule
+    val animatorTestRule = AnimatorTestRule(this)
     private val property: PhysicsProperty =
         PhysicsProperty(R.id.scale_x_animator_tag, effectiveProperty)
     private var finishListener: DynamicAnimation.OnAnimationEndListener? = null
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/ConversationCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index 2853264..fa185bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.Flags
 import android.app.NotificationChannel
+import android.app.NotificationChannel.SYSTEM_RESERVED_IDS
 import android.app.NotificationManager.IMPORTANCE_DEFAULT
 import android.app.NotificationManager.IMPORTANCE_HIGH
 import android.app.NotificationManager.IMPORTANCE_LOW
@@ -124,6 +125,14 @@
     }
 
     @Test
+    fun testPrioritySectioner_doesNotClaim_classifiedConversation() {
+        val sectioner = coordinator.priorityPeopleSectioner
+        for (id in SYSTEM_RESERVED_IDS) {
+            assertFalse(sectioner.isInSection(makeClassifiedConversation(id)))
+        }
+    }
+
+    @Test
     fun testPromotesImportantConversations() {
         assertTrue(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON)))
         assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_FULL_PERSON)))
@@ -166,6 +175,14 @@
     }
 
     @Test
+    fun testAlertingSectioner_doesNotClaim_classifiedConversation() {
+        val sectioner = coordinator.peopleAlertingSectioner
+        for (id in SYSTEM_RESERVED_IDS) {
+            assertFalse(sectioner.isInSection(makeClassifiedConversation(id)))
+        }
+    }
+
+    @Test
     fun testInAlertingPeopleSectionWhenTheImportanceIsAtLeastDefault() {
         // GIVEN
         val alertingEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_DEFAULT) }
@@ -186,6 +203,15 @@
 
     @Test
     @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
+    fun testSilentSectioner_doesNotClaim_classifiedConversation() {
+        val sectioner = coordinator.peopleSilentSectioner
+        for (id in SYSTEM_RESERVED_IDS) {
+            assertFalse(sectioner.isInSection(makeClassifiedConversation(id)))
+        }
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
     fun testInSilentPeopleSectionWhenTheImportanceIsLowerThanDefault() {
         // GIVEN
         val silentEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_LOW) }
@@ -280,4 +306,17 @@
         assertEquals(type, peopleNotificationIdentifier.getPeopleNotificationType(entry))
         return entry
     }
+
+    private fun makeClassifiedConversation(channelId: String): NotificationEntry {
+        val channel = NotificationChannel(channelId, channelId, IMPORTANCE_LOW)
+        val entry =
+            NotificationEntryBuilder()
+                .updateRanking {
+                    it.setIsConversation(true)
+                    it.setShortcutInfo(mock())
+                    it.setChannel(channel)
+                }
+                .build()
+        return entry
+    }
 }
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/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
index 961616c..3d1fdee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
@@ -22,6 +22,8 @@
 import com.android.systemui.accessibility.data.repository.FakeAccessibilityRepository
 import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.testKosmos
+import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -32,12 +34,16 @@
 @SmallTest
 class ActivatableNotificationViewModelTest : SysuiTestCase() {
 
+    private val kosmos = testKosmos()
+
     // fakes
     private val a11yRepo = FakeAccessibilityRepository()
 
     // real impls
     private val a11yInteractor = AccessibilityInteractor(a11yRepo)
-    private val underTest = ActivatableNotificationViewModel(a11yInteractor)
+    private val windowRootViewBlurInteractor = kosmos.windowRootViewBlurInteractor
+    private val underTest = ActivatableNotificationViewModel(a11yInteractor,
+        windowRootViewBlurInteractor)
 
     @Test
     fun isTouchable_whenA11yTouchExplorationDisabled() = runTest {
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..cb4642c 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,18 @@
 
 package com.android.systemui.statusbar.notification.stack
 
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
 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
@@ -40,6 +46,7 @@
 import org.mockito.Mockito.description
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.doNothing
 
 private const val VIEW_HEIGHT = 100
 private const val FULL_SHADE_APPEAR_TRANSLATION = 300
@@ -50,17 +57,19 @@
 @RunWithLooper
 class StackStateAnimatorTest : SysuiTestCase() {
 
+    @get:Rule val setFlagsRule = SetFlagsRule()
     @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 =
-        ExpandableViewState().apply { height = VIEW_HEIGHT }
+    private lateinit var viewState: ExpandableViewState
     private val runnableCaptor: ArgumentCaptor<Runnable> = argumentCaptor()
 
     @Before
     fun setUp() {
+        viewState = ExpandableViewState().apply { height = VIEW_HEIGHT }
         overrideResource(
             R.dimen.go_to_full_shade_appearing_translation,
             FULL_SHADE_APPEAR_TRANSLATION,
@@ -69,11 +78,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 +112,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 =
@@ -117,8 +158,35 @@
             )
     }
 
+    @DisableFlags(Flags.FLAG_PHYSICAL_NOTIFICATION_MOVEMENT)
     @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)
@@ -147,6 +215,62 @@
     }
 
     @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)
+        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()
+    }
+
+    @EnableFlags(Flags.FLAG_PHYSICAL_NOTIFICATION_MOVEMENT)
+    @Test
+    fun startAnimationForEvents_startsHeadsUpDisappearAnim_physical() {
+        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),
+            )
+
+        runnableCaptor.value.run() // execute the end runnable
+        verify(view, description("should be called at the end of the disappear animation"))
+            .removeFromTransientContainer()
+    }
+
+    @Test
     fun initView_updatesResources() {
         // Given: the resource values are initialized in the SSA
         assertThat(stackStateAnimator.mGoToFullShadeAppearingTranslation)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
index ef415c9..6ee1c4d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.stack
 
+import android.animation.AnimatorTestRule
 import android.animation.ValueAnimator
 import android.view.View
 import androidx.test.annotation.UiThreadTest
@@ -28,6 +29,7 @@
 import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator.Companion.TAG_ANIMATOR_TRANSLATION_Y
 import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator.Companion.Y_TRANSLATION
 import org.junit.Assert
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import kotlin.math.log2
@@ -38,6 +40,8 @@
 @UiThreadTest
 class ViewStateTest : SysuiTestCase() {
     private val viewState = ViewState(true /* usePhysicsForMovement */)
+    @get:Rule
+    val animatorTestRule = AnimatorTestRule(this)
 
     @Suppress("DIVISION_BY_ZERO")
     @Test
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/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6ff1240..43ea2c3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1024,6 +1024,12 @@
     <string name="hearing_devices_ambient_control_left">Left</string>
     <!-- QuickSettings: The text to show the control is for right side device. [CHAR LIMIT=30] -->
     <string name="hearing_devices_ambient_control_right">Right</string>
+    <!-- QuickSettings: Content description for unified ambient control slider. [CHAR LIMIT=NONE] -->
+    <string name="hearing_devices_ambient_control_description">Surroundings</string>
+    <!-- QuickSettings: Content description for left ambient control slider. [CHAR LIMIT=NONE] -->
+    <string name="hearing_devices_ambient_control_left_description">Left surroundings</string>
+    <!-- QuickSettings: Content description for left ambient control slider. [CHAR LIMIT=NONE] -->
+    <string name="hearing_devices_ambient_control_right_description">Right surroundings</string>
     <!-- QuickSettings: Content description for a button, that expands ambient volume sliders [CHAR_LIMIT=NONE] -->
     <string name="hearing_devices_ambient_expand_controls">Expand to left and right separated controls</string>
     <!-- QuickSettings: Content description for a button, that collapses ambient volume sliders [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 5b96e50..4431dda 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -871,6 +871,7 @@
     </style>
 
     <style name="MediaPlayer.SessionAction.Primary" parent="MediaPlayer.SessionAction">
+        <item name="android:tint">@android:color/system_on_primary_dark</item>
         <item name="android:background">@drawable/qs_media_round_button_background</item>
         <item name="android:backgroundTint">@color/media_player_solid_button_bg</item>
     </style>
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/AmbientVolumeLayout.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
index 5247acc..33c9eb1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
@@ -327,8 +327,19 @@
         slider.addOnChangeListener(mSliderOnChangeListener);
         if (side == SIDE_LEFT) {
             slider.setTitle(mContext.getString(R.string.hearing_devices_ambient_control_left));
+            slider.setContentDescription(
+                    mContext.getString(R.string.hearing_devices_ambient_control_left));
+            slider.setSliderContentDescription(
+                    mContext.getString(R.string.hearing_devices_ambient_control_left_description));
         } else if (side == SIDE_RIGHT) {
             slider.setTitle(mContext.getString(R.string.hearing_devices_ambient_control_right));
+            slider.setContentDescription(
+                    mContext.getString(R.string.hearing_devices_ambient_control_right));
+            slider.setSliderContentDescription(
+                    mContext.getString(R.string.hearing_devices_ambient_control_right_description));
+        } else {
+            slider.setSliderContentDescription(
+                    mContext.getString(R.string.hearing_devices_ambient_control_description));
         }
         mSideToSliderMap.put(side, slider);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java
index 1a068c4..5c0ad3d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java
@@ -92,6 +92,11 @@
         mSlider = requireViewById(R.id.ambient_volume_slider);
         mSlider.addOnSliderTouchListener(mSliderTouchListener);
         mSlider.addOnChangeListener(mSliderChangeListener);
+
+        setFocusable(false);
+        setClickable(false);
+        mSlider.setFocusable(false);
+        mSlider.setClickable(false);
     }
 
     /**
@@ -178,6 +183,13 @@
         return (int) Math.ceil((value - min) / levelGap);
     }
 
+    /** Sets the content description to the ambient volume slider. */
+    public void setSliderContentDescription(CharSequence contentDescription) {
+        if (mSlider != null) {
+            mSlider.setContentDescription(contentDescription);
+        }
+    }
+
     /** Interface definition for a callback invoked when a slider's value is changed. */
     public interface OnChangeListener {
         /** Called when the finger is take off from the slider. */
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/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
deleted file mode 100644
index b531d15..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.ui.viewmodel
-
-import android.annotation.SuppressLint
-import android.app.DreamManager
-import android.content.Intent
-import android.provider.Settings
-import androidx.compose.runtime.getValue
-import com.android.internal.logging.UiEventLogger
-import com.android.systemui.communal.domain.interactor.CommunalPrefsInteractor
-import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
-import com.android.systemui.communal.shared.log.CommunalUiEvent
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.lifecycle.Hydrator
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.policy.BatteryController
-import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
-import com.android.systemui.util.kotlin.BooleanFlowOperators.not
-import com.android.systemui.util.kotlin.isDevicePluggedIn
-import com.android.systemui.util.kotlin.sample
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.receiveAsFlow
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-class CommunalToDreamButtonViewModel
-@AssistedInject
-constructor(
-    @Background private val backgroundContext: CoroutineContext,
-    batteryController: BatteryController,
-    private val prefsInteractor: CommunalPrefsInteractor,
-    private val settingsInteractor: CommunalSettingsInteractor,
-    private val activityStarter: ActivityStarter,
-    private val dreamManager: DreamManager,
-    private val uiEventLogger: UiEventLogger,
-) : ExclusiveActivatable() {
-
-    private val hydrator = Hydrator("CommunalToDreamButtonViewModel.hydrator")
-    private val _requests = Channel<Unit>(Channel.BUFFERED)
-
-    /** Whether we should show a button on hub to switch to dream. */
-    val shouldShowDreamButtonOnHub: Boolean by
-        hydrator.hydratedStateOf(
-            traceName = "shouldShowDreamButtonOnHub",
-            initialValue = false,
-            source = batteryController.isDevicePluggedIn().distinctUntilChanged(),
-        )
-
-    /** Return whether to show the dream button tooltip. */
-    val shouldShowTooltip: Boolean by
-        hydrator.hydratedStateOf(
-            traceName = "shouldShowTooltip",
-            initialValue = false,
-            source =
-                allOf(
-                    not(prefsInteractor.isDreamButtonTooltipDismissed),
-                    prefsInteractor.isHubOnboardingDismissed,
-                ),
-        )
-
-    /** Set the dream button tooltip to be dismissed. */
-    fun setDreamButtonTooltipDismissed() {
-        prefsInteractor.setDreamButtonTooltipDismissed()
-    }
-
-    /** Handle a tap on the "show dream" button. */
-    fun onShowDreamButtonTap() {
-        uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_SHOW_DREAM_BUTTON_TAP)
-        _requests.trySend(Unit)
-    }
-
-    @SuppressLint("MissingPermission")
-    override suspend fun onActivated(): Nothing = coroutineScope {
-        launch {
-            _requests
-                .receiveAsFlow()
-                .sample(settingsInteractor.isScreensaverEnabled)
-                .collectLatest { enabled ->
-                    withContext(backgroundContext) {
-                        if (enabled) {
-                            dreamManager.startDream()
-                        } else {
-                            activityStarter.postStartActivityDismissingKeyguard(
-                                Intent(Settings.ACTION_DREAM_SETTINGS),
-                                0,
-                            )
-                        }
-                    }
-                }
-        }
-
-        launch { hydrator.activate() }
-
-        awaitCancellation()
-    }
-
-    @AssistedFactory
-    interface Factory {
-        fun create(): CommunalToDreamButtonViewModel
-    }
-}
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/controller/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
index 9b443f5..5d62c02 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
@@ -90,15 +90,16 @@
 
     fun logCarouselVisible() = buffer.log(TAG, LogLevel.DEBUG, {}, { "showing carousel" })
 
-    fun logMediaHostVisibility(location: Int, visible: Boolean) {
+    fun logMediaHostVisibility(location: Int, visible: Boolean, oldState: Boolean) {
         buffer.log(
             TAG,
             LogLevel.DEBUG,
             {
                 int1 = location
                 bool1 = visible
+                bool2 = oldState
             },
-            { "media host visibility changed location=$location, visible:$visible" },
+            { "media host visibility changed location=$location, visible:$visible, was:$oldState" },
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index c689408..69006c6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -1129,10 +1129,11 @@
         traceSection("MediaHierarchyManager#updateHostAttachment") {
             if (SceneContainerFlag.isEnabled) {
                 // No need to manage transition states - just update the desired location directly
-                logger.logMediaHostAttachment(desiredLocation)
+                val host = getHost(desiredLocation)
+                logger.logMediaHostAttachment(desiredLocation, host?.visible)
                 mediaCarouselController.onDesiredLocationChanged(
                     desiredLocation = desiredLocation,
-                    desiredHostState = getHost(desiredLocation),
+                    desiredHostState = host,
                     animate = false,
                 )
                 return
@@ -1169,7 +1170,8 @@
                     // that and directly set the mediaFrame's bounds within the premeasured host.
                     targetHost.addView(mediaFrame)
                 }
-                logger.logMediaHostAttachment(currentAttachmentLocation)
+                val host = getHost(currentAttachmentLocation)
+                logger.logMediaHostAttachment(currentAttachmentLocation, host?.visible)
                 if (isCrossFadeAnimatorRunning) {
                     // When cross-fading with an animation, we only notify the media carousel of the
                     // location change, once the view is reattached to the new place and not
@@ -1313,6 +1315,7 @@
                 isHomeScreenShadeVisibleToUser() ||
                 isGlanceableHubVisibleToUser()
         val mediaVisible = qsExpanded || hasActiveMediaOrRecommendation
+        logger.logUserVisibilityChange(shadeVisible, mediaVisible)
         mediaCarouselController.mediaCarouselScrollHandler.visibleToUser =
             shadeVisible && mediaVisible
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
index 1514db3..089d16b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
@@ -36,7 +36,7 @@
                 int1 = width
                 int2 = height
             },
-            { "size ($str1): $int1 x $int2" }
+            { "size ($str1): $int1 x $int2" },
         )
     }
 
@@ -49,11 +49,31 @@
                 int1 = startLocation
                 int2 = endLocation
             },
-            { "location ($str1): $int1 -> $int2" }
+            { "location ($str1): $int1 -> $int2" },
         )
     }
 
-    fun logMediaHostAttachment(host: Int) {
-        buffer.log(TAG, LogLevel.DEBUG, { int1 = host }, { "Host (updateHostAttachment): $int1" })
+    fun logMediaHostAttachment(host: Int, visible: Boolean?) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                int1 = host
+                str1 = visible.toString()
+            },
+            { "Host (updateHostAttachment): $int1 visible $str1" },
+        )
+    }
+
+    fun logUserVisibilityChange(shadeVisible: Boolean, mediaVisible: Boolean) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                bool1 = shadeVisible
+                bool2 = mediaVisible
+            },
+            { "User visibility shade: $shadeVisible media: $mediaVisible" },
+        )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
index 11251cd..a518349 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
@@ -208,6 +208,7 @@
      * the visibility has changed
      */
     fun updateViewVisibility() {
+        val oldState = state.visible
         state.visible =
             if (mediaCarouselController.isLockedAndHidden()) {
                 false
@@ -217,9 +218,9 @@
                 mediaDataManager.hasAnyMediaOrRecommendation()
             }
         val newVisibility = if (visible) View.VISIBLE else View.GONE
-        if (newVisibility != hostView.visibility) {
+        if (oldState != state.visible || newVisibility != hostView.visibility) {
             hostView.visibility = newVisibility
-            debugLogger.logMediaHostVisibility(location, visible)
+            debugLogger.logMediaHostVisibility(location, visible, oldState)
             visibleChangedListeners.forEach { it.invoke(visible) }
         }
     }
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 da7053b..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 = 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/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/PhysicsPropertyAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt
index 74faf25..0a24d7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt
@@ -15,12 +15,12 @@
  */
 package com.android.systemui.statusbar.notification
 
+import android.util.FloatProperty
 import android.util.Property
 import android.view.View
-import androidx.dynamicanimation.animation.DynamicAnimation
-import androidx.dynamicanimation.animation.FloatPropertyCompat
-import androidx.dynamicanimation.animation.SpringAnimation
-import androidx.dynamicanimation.animation.SpringForce
+import com.android.internal.dynamicanimation.animation.DynamicAnimation
+import com.android.internal.dynamicanimation.animation.SpringAnimation
+import com.android.internal.dynamicanimation.animation.SpringForce
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator.Companion.createDefaultSpring
 import com.android.systemui.statusbar.notification.stack.AnimationProperties
@@ -33,8 +33,8 @@
  */
 data class PhysicsProperty(val tag: Int, val property: Property<View, Float>) {
     val offsetProperty =
-        object : FloatPropertyCompat<View>(property.name) {
-            override fun getValue(view: View): Float {
+        object : FloatProperty<View>(property.name) {
+            override fun get(view: View): Float {
                 return property.get(view)
             }
 
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/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index 248b528..28923b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.collection.coordinator
 
+import android.app.NotificationChannel.SYSTEM_RESERVED_IDS
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.PipelineEntry
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -89,6 +90,7 @@
             object : NotifSectioner("Priority People", BUCKET_PRIORITY_PEOPLE) {
                 override fun isInSection(entry: PipelineEntry): Boolean {
                     return getPeopleType(entry) == TYPE_IMPORTANT_PERSON
+                            && entry.representativeEntry?.channel?.id !in SYSTEM_RESERVED_IDS
                 }
             }
 
@@ -96,10 +98,12 @@
     val peopleAlertingSectioner = object : NotifSectioner("People(alerting)", BUCKET_PEOPLE) {
         override fun isInSection(entry: PipelineEntry): Boolean  {
             if (SortBySectionTimeFlag.isEnabled) {
-                return highPriorityProvider.isHighPriorityConversation(entry)
-                        || isConversation(entry)
+                return (highPriorityProvider.isHighPriorityConversation(entry)
+                        || isConversation(entry))
+                        && entry.representativeEntry?.channel?.id !in SYSTEM_RESERVED_IDS
             } else {
                 return highPriorityProvider.isHighPriorityConversation(entry)
+                        && entry.representativeEntry?.channel?.id !in SYSTEM_RESERVED_IDS
             }
         }
 
@@ -111,11 +115,12 @@
     }
 
     val peopleSilentSectioner = object : NotifSectioner("People(silent)", BUCKET_PEOPLE) {
-        // Because the peopleAlertingSectioner is above this one, it will claim all conversations that are alerting.
-        // All remaining conversations must be silent.
+        // Because the peopleAlertingSectioner is above this one, it will claim all conversations
+        // that are alerting. All remaining conversations must be silent.
         override fun isInSection(entry: PipelineEntry): Boolean {
             SortBySectionTimeFlag.assertInLegacyMode()
             return isConversation(entry)
+                    && entry.representativeEntry?.channel?.id !in SYSTEM_RESERVED_IDS
         }
 
         override fun getComparator(): NotifComparator {
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/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
index 7959e99..2a01a14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
@@ -20,6 +20,7 @@
 import android.app.Flags.notificationsRedesignTemplates
 import android.app.Notification
 import android.graphics.PorterDuff
+import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.View.GONE
@@ -80,9 +81,13 @@
     val content = viewModel.content ?: return
     val audiblyAlertedIconVisible = viewModel.audiblyAlertedIconVisible
 
-    key(content.identity) {
-        val layoutResource = content.layoutResource ?: return
+    val layoutResource = content.layoutResource
+    if (layoutResource == null) {
+        Log.w(TAG, "not displaying promoted notif with ineligible style on AOD")
+        return
+    }
 
+    key(content.identity) {
         val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
         val sidePaddingValues = PaddingValues(horizontal = sidePaddings, vertical = 0.dp)
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index a081ad5..6837cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -119,6 +119,7 @@
     protected Point mTargetPoint;
     private boolean mDismissed;
     private boolean mRefocusOnDismiss;
+    protected boolean mIsBlurSupported;
 
     public ActivatableNotificationView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -128,12 +129,13 @@
     }
 
     private void updateColors() {
-        if (notificationRowTransparency()) {
+        if (usesTransparentBackground()) {
             mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
         } else {
             mNormalColor = mContext.getColor(
                     com.android.internal.R.color.materialColorSurfaceContainerHigh);
         }
+        setBackgroundToNormalColor();
         mTintedRippleColor = mContext.getColor(
                 R.color.notification_ripple_tinted_color);
         mNormalRippleColor = mContext.getColor(
@@ -144,6 +146,12 @@
         mOverrideAmount = 0.0f;
     }
 
+    private void setBackgroundToNormalColor() {
+        if (mBackgroundNormal != null) {
+            mBackgroundNormal.setNormalColor(mNormalColor);
+        }
+    }
+
     /**
      * Reload background colors from resources and invalidate views.
      */
@@ -173,6 +181,7 @@
         mBackgroundNormal = findViewById(R.id.backgroundNormal);
         mFakeShadow = findViewById(R.id.fake_shadow);
         mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
+        setBackgroundToNormalColor();
         initBackground();
         updateBackgroundTint();
         updateOutlineAlpha();
@@ -326,6 +335,21 @@
         mBackgroundNormal.setBottomAmountClips(!isChildInGroup());
     }
 
+    public void setIsBlurSupported(boolean isBlurSupported) {
+        if (!notificationRowTransparency()) {
+            return;
+        }
+        boolean usedTransparentBackground = usesTransparentBackground();
+        mIsBlurSupported = isBlurSupported;
+        if (usedTransparentBackground != usesTransparentBackground()) {
+            updateBackgroundColors();
+        }
+    }
+
+    protected boolean usesTransparentBackground() {
+        return mIsBlurSupported && notificationRowTransparency();
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
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 35e84e5..3ef1fd2 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
@@ -20,8 +20,8 @@
 import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 
-import static com.android.systemui.Flags.notificationsPinnedHunInShade;
 import static com.android.systemui.Flags.notificationRowTransparency;
+import static com.android.systemui.Flags.notificationsPinnedHunInShade;
 import static com.android.systemui.flags.Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE;
 import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
 import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
@@ -1678,11 +1678,17 @@
         if (view != null) {
             view.setBackgroundTintColor(color);
         }
-        if (notificationRowTransparency()
-                && (mBackgroundNormal != null)
-                && (mEntry != null)) {
-            mBackgroundNormal.setBgIsColorized(
-                    mEntry.getSbn().getNotification().isColorized());
+        if (notificationRowTransparency() && mBackgroundNormal != null) {
+            if (NotificationBundleUi.isEnabled()) {
+                mBackgroundNormal.setBgIsColorized(
+                        usesTransparentBackground() && mEntryAdapter.isColorized());
+            } else {
+                if (mEntry != null) {
+                    mBackgroundNormal.setBgIsColorized(
+                            usesTransparentBackground()
+                                    && mEntry.getSbn().getNotification().isColorized());
+                }
+            }
         }
     }
 
@@ -2374,7 +2380,11 @@
             return traceTag;
         }
 
-        return traceTag + "(" + getEntry().getNotificationStyle() + ")";
+        if (NotificationBundleUi.isEnabled()) {
+            return traceTag + "(" + getEntryAdapter().getStyle() + ")";
+        } else {
+            return traceTag + "(" + getEntry().getNotificationStyle() + ")";
+        }
     }
 
     @Override
@@ -3012,6 +3022,7 @@
 
         mUserLocked = userLocked;
         mPrivateLayout.setUserExpanding(userLocked);
+        mPublicLayout.setUserExpanding(userLocked);
         // This is intentionally not guarded with mIsSummaryWithChildren since we might have had
         // children but not anymore.
         if (mChildrenContainer != null) {
@@ -3698,8 +3709,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
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 4978fa4..e1219e8 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
@@ -73,7 +73,7 @@
     private int mDrawableAlpha = 255;
     private final ColorStateList mLightColoredStatefulColors;
     private final ColorStateList mDarkColoredStatefulColors;
-    private final int mNormalColor;
+    private int mNormalColor;
     private boolean mBgIsColorized = false;
     private boolean mForceOpaque = false;
     private final int convexR = 9;
@@ -89,15 +89,13 @@
                 R.color.notification_state_color_light);
         mDarkColoredStatefulColors = getResources().getColorStateList(
                 R.color.notification_state_color_dark);
-        if (notificationRowTransparency()) {
-            mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
-        } else  {
-            mNormalColor = mContext.getColor(
-                    com.android.internal.R.color.materialColorSurfaceContainerHigh);
-        }
         mFocusOverlayStroke = getResources().getDimension(R.dimen.notification_focus_stroke_width);
     }
 
+    public void setNormalColor(int color) {
+        mNormalColor = color;
+    }
+
     @Override
     public void onTargetVisibilityChanged(boolean targetVisible) {
         if (NotificationAddXOnHoverToDismiss.isUnexpectedlyInLegacyMode()) {
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/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/ui/viewbinder/ActivatableNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
index f0b5c36..8984f2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
@@ -50,6 +50,15 @@
                 view.registerListenersWhileAttached(touchHandler)
             }
         }
+        view.repeatWhenAttached {
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch {
+                    viewModel.isBlurSupported.collect { supported ->
+                        view.setIsBlurSupported(supported)
+                    }
+                }
+            }
+        }
     }
 
     private suspend fun ActivatableNotificationView.registerListenersWhileAttached(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt
index f46d424..9a86ffb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.row.ui.viewmodel
 
 import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
+import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
 import dagger.Module
 import dagger.Provides
 import kotlinx.coroutines.flow.Flow
@@ -26,25 +27,34 @@
 interface ActivatableNotificationViewModel : ExpandableOutlineViewModel {
     /** Does the view react to touches? */
     val isTouchable: Flow<Boolean>
+    val isBlurSupported: Flow<Boolean>
 
     companion object {
         operator fun invoke(
             a11yInteractor: AccessibilityInteractor,
-        ): ActivatableNotificationViewModel = ActivatableNotificationViewModelImpl(a11yInteractor)
+            windowRootViewBlurInteractor: WindowRootViewBlurInteractor
+        ): ActivatableNotificationViewModel =
+            ActivatableNotificationViewModelImpl(a11yInteractor, windowRootViewBlurInteractor)
     }
 }
 
 private class ActivatableNotificationViewModelImpl(
     a11yInteractor: AccessibilityInteractor,
+    windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
 ) : ActivatableNotificationViewModel {
     override val isTouchable: Flow<Boolean> =
         // If a11y touch exploration is enabled, then the activatable view should ignore touches
         a11yInteractor.isTouchExplorationEnabled.map { !it }
+    override val isBlurSupported: Flow<Boolean> =
+        windowRootViewBlurInteractor.isBlurCurrentlySupported
 }
 
 @Module
 object ActivatableNotificationViewModelModule {
     @Provides
-    fun provideViewModel(interactor: AccessibilityInteractor) =
-        ActivatableNotificationViewModel(interactor)
+    fun provideViewModel(
+        a11yInteractor: AccessibilityInteractor,
+        windowRootViewBlurInteractor: WindowRootViewBlurInteractor
+    ) =
+        ActivatableNotificationViewModel(a11yInteractor, windowRootViewBlurInteractor)
 }
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/AnimationProperties.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
index 3d60092..4d01cbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
@@ -23,7 +23,7 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
-import androidx.dynamicanimation.animation.DynamicAnimation;
+import com.android.internal.dynamicanimation.animation.DynamicAnimation;
 
 import java.util.function.Consumer;
 
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..19abfa8 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,18 +27,20 @@
 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;
 
-import androidx.dynamicanimation.animation.DynamicAnimation;
-
 import com.android.app.animation.Interpolators;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.dynamicanimation.animation.DynamicAnimation;
 import com.android.systemui.res.R;
 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 +85,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 +109,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 +552,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 +736,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 +826,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/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index 2ef6f36..29dbeb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -30,10 +30,9 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringAnimation;
-
 import com.android.app.animation.Interpolators;
+import com.android.internal.dynamicanimation.animation.DynamicAnimation;
+import com.android.internal.dynamicanimation.animation.SpringAnimation;
 import com.android.systemui.Dumpable;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
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/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
deleted file mode 100644
index 43d3eb7..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.ui.viewmodel
-
-import android.service.dream.dreamManager
-import com.android.internal.logging.uiEventLogger
-import com.android.systemui.communal.domain.interactor.communalPrefsInteractor
-import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.plugins.activityStarter
-import com.android.systemui.statusbar.policy.batteryController
-
-val Kosmos.communalToDreamButtonViewModel by
-    Kosmos.Fixture {
-        CommunalToDreamButtonViewModel(
-            backgroundContext = testDispatcher,
-            batteryController = batteryController,
-            prefsInteractor = communalPrefsInteractor,
-            settingsInteractor = communalSettingsInteractor,
-            activityStarter = activityStarter,
-            dreamManager = dreamManager,
-            uiEventLogger = uiEventLogger,
-        )
-    }
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/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt
index 2523975..7ccbdb7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt
@@ -19,9 +19,11 @@
 import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
 
 val Kosmos.activatableNotificationViewModel by Fixture {
     ActivatableNotificationViewModel.invoke(
         a11yInteractor = accessibilityInteractor,
+        windowRootViewBlurInteractor = windowRootViewBlurInteractor,
     )
 }
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/asm/AsmUtils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index 735635c..b41ce0f 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -268,18 +268,45 @@
 }
 
 /**
+ * Write bytecode to pop the 2 uninitialized instances out of the stack
+ * after performing constructor redirection.
+ */
+fun adjustStackForConstructorRedirection(writer: MethodVisitor) {
+    // Stack: { uninitialized, uninitialized, obj }
+    writer.visitInsn(Opcodes.SWAP)
+    // Stack: { uninitialized, obj, uninitialized }
+    writer.visitInsn(Opcodes.POP)
+    // Stack: { uninitialized, obj }
+    writer.visitInsn(Opcodes.SWAP)
+    // Stack: { obj, uninitialized }
+    writer.visitInsn(Opcodes.POP)
+    // Stack: { obj }
+
+    // We end up with only the desired object on the stack
+}
+
+/**
  * Given a method descriptor, insert an [argType] as the first argument to it.
  */
 fun prependArgTypeToMethodDescriptor(methodDescriptor: String, classInternalName: String): String {
     val returnType = Type.getReturnType(methodDescriptor)
     val argTypes = Type.getArgumentTypes(methodDescriptor).toMutableList()
 
-    argTypes.add(0, Type.getType("L" + classInternalName + ";"))
+    argTypes.add(0, Type.getType("L$classInternalName;"))
 
     return Type.getMethodDescriptor(returnType, *argTypes.toTypedArray())
 }
 
 /**
+ * Given a method descriptor, change the return type to [classInternalName].
+ */
+fun changeMethodDescriptorReturnType(methodDescriptor: String, classInternalName: String): String {
+    val argTypes = Type.getArgumentTypes(methodDescriptor)
+    val returnType = Type.getType("L$classInternalName;")
+    return Type.getMethodDescriptor(returnType, *argTypes)
+}
+
+/**
  * Return the "visibility" modifier from an `access` integer.
  *
  * (see https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1-200-E.1)
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/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index d0c97c0..dd353e9 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -100,7 +100,6 @@
         methodName: String,
         methodDesc: String,
         replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
-        policy: FilterPolicyWithReason,
     )
 }
 
@@ -286,9 +285,10 @@
             methodName: String,
             methodDesc: String,
             replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
-            policy: FilterPolicyWithReason,
         ) {
-            imf.setPolicyForMethod(className, methodName, methodDesc, policy)
+            // Keep the source method, because the target method may call it.
+            imf.setPolicyForMethod(className, methodName, methodDesc,
+                FilterPolicy.Keep.withReason(FILTER_REASON))
             methodReplaceSpec.add(replaceSpec)
         }
     }
@@ -642,7 +642,6 @@
                     methodName,
                     signature,
                     spec,
-                    policyWithReason,
                 )
             } else {
                 // It's an in-class replace.
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/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
index 70e7d46..b8a3576 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
@@ -17,7 +17,10 @@
 
 import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
 import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
+import com.android.hoststubgen.asm.CTOR_NAME
 import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.adjustStackForConstructorRedirection
+import com.android.hoststubgen.asm.changeMethodDescriptorReturnType
 import com.android.hoststubgen.asm.prependArgTypeToMethodDescriptor
 import com.android.hoststubgen.asm.writeByteCodeToPushArguments
 import com.android.hoststubgen.asm.writeByteCodeToReturn
@@ -33,6 +36,7 @@
 import org.objectweb.asm.MethodVisitor
 import org.objectweb.asm.Opcodes
 import org.objectweb.asm.Opcodes.INVOKEINTERFACE
+import org.objectweb.asm.Opcodes.INVOKESPECIAL
 import org.objectweb.asm.Opcodes.INVOKESTATIC
 import org.objectweb.asm.Opcodes.INVOKEVIRTUAL
 import org.objectweb.asm.Type
@@ -376,53 +380,90 @@
         val callerMethodName: String,
         next: MethodVisitor?,
     ) : MethodVisitor(OPCODE_VERSION, next) {
-        override fun visitMethodInsn(
+
+        private fun doReplace(
             opcode: Int,
-            owner: String?,
-            name: String?,
-            descriptor: String?,
-            isInterface: Boolean,
-        ) {
+            owner: String,
+            name: String,
+            descriptor: String,
+        ): Boolean {
             when (opcode) {
                 INVOKESTATIC, INVOKEVIRTUAL, INVOKEINTERFACE -> {}
-                else -> {
-                    // Don't touch other opcodes.
-                    super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
-                    return
-                }
+                // We only support INVOKESPECIAL when replacing constructors.
+                INVOKESPECIAL -> if (name != CTOR_NAME) return false
+                // Don't touch other opcodes.
+                else -> return false
             }
+
             val to = filter.getMethodCallReplaceTo(
-                currentClassName, callerMethodName, owner!!, name!!, descriptor!!
+                currentClassName, callerMethodName, owner, name, descriptor
             )
 
             if (to == null
                 // Don't replace if the target is the callsite.
                 || (to.className == currentClassName && to.methodName == callerMethodName)
             ) {
-                super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
-                return
+                return false
             }
 
-            // Replace the method call with a (static) call to the target method.
-            // If it's a non-static call, the target method's first argument will receive "this".
-            // (Because of that, we don't need to manipulate the stack. Just replace the
-            // method call.)
+            if (opcode != INVOKESPECIAL) {
+                // It's either a static method call or virtual method call.
+                // Either way, we don't manipulate the stack and send the original arguments
+                // as is to the target method.
+                //
+                // If the call is a virtual call (INVOKEVIRTUAL or INVOKEINTERFACE), then
+                // the first argument in the stack is the "this" object, so the target
+                // method must have an extra argument as the first argument to receive it.
+                // We update the method descriptor with prependArgTypeToMethodDescriptor()
+                // to absorb this difference.
 
-            val toDesc = if (opcode == INVOKESTATIC) {
-                // Static call to static call, no need to change the desc.
-                descriptor
+                val toDesc = if (opcode == INVOKESTATIC) {
+                    descriptor
+                } else {
+                    prependArgTypeToMethodDescriptor(descriptor, owner)
+                }
+
+                mv.visitMethodInsn(
+                    INVOKESTATIC,
+                    to.className,
+                    to.methodName,
+                    toDesc,
+                    false
+                )
             } else {
-                // Need to prepend the "this" type to the descriptor.
-                prependArgTypeToMethodDescriptor(descriptor, owner)
+                // Because an object initializer does not return a value, the newly created
+                // but uninitialized object will be dup-ed at the bottom of the stack.
+                // We first call the target method to consume the constructor arguments at the top.
+
+                val toDesc = changeMethodDescriptorReturnType(descriptor, owner)
+
+                // Before stack: { uninitialized, uninitialized, args... }
+                mv.visitMethodInsn(
+                    INVOKESTATIC,
+                    to.className,
+                    to.methodName,
+                    toDesc,
+                    false
+                )
+                // After stack: { uninitialized, uninitialized, obj }
+
+                // Next we pop the 2 uninitialized instances out of the stack.
+                adjustStackForConstructorRedirection(mv)
             }
 
-            mv.visitMethodInsn(
-                INVOKESTATIC,
-                to.className,
-                to.methodName,
-                toDesc,
-                false
-            )
+            return true
+        }
+
+        override fun visitMethodInsn(
+            opcode: Int,
+            owner: String,
+            name: String,
+            descriptor: String,
+            isInterface: Boolean,
+        ) {
+            if (!doReplace(opcode, owner, name, descriptor)) {
+                super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
+            }
         }
     }
 }
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..2b942a9 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
@@ -1883,6 +1883,42 @@
 InnerClasses:
   public static #x= #x of #x;          // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
   public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+  Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+  minor version: 0
+  major version: 65
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
+  public int i;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iload_1
+         x: putfield      #x                  // Field i:I
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+            0      10     1     i   I
+}
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+InnerClasses:
+  public static #x= #x of #x;           // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
   Compiled from "TinyFrameworkMethodCallReplace.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -1891,7 +1927,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 3
+  interfaces: 0, fields: 0, methods: 4, attributes: 3
 Constant pool:
 {
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
@@ -1937,10 +1973,28 @@
         Start  Length  Slot  Name   Signature
             0       4     0     a   I
             0       4     1     b   I
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+    descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+         x: dup
+         x: iload_0
+         x: iconst_1
+         x: iadd
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      11     0     i   I
 }
 SourceFile: "TinyFrameworkMethodCallReplace.java"
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
 InnerClasses:
+  public static #x= #x of #x;          // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   public static #x= #x of #x;          // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class
   Compiled from "TinyFrameworkMethodCallReplace.java"
@@ -1950,7 +2004,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 5, attributes: 5
+  interfaces: 0, fields: 0, methods: 6, attributes: 5
 Constant pool:
 {
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
@@ -2008,6 +2062,21 @@
          x: ireturn
       LineNumberTable:
 
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+    descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+         x: dup
+         x: iload_0
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       9     0     i   I
+
   private static int originalAdd(int, int);
     descriptor: (II)I
     flags: (0x000a) ACC_PRIVATE, ACC_STATIC
@@ -2046,6 +2115,7 @@
     android.hosttest.annotation.HostSideTestWholeClassKeep
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
 BootstrapMethods:
   x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
     Method arguments:
@@ -2053,8 +2123,9 @@
       #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.lambda$nonStaticMethodCallReplaceTester$0:(Ljava/util/concurrent/atomic/AtomicBoolean;)V
       #x ()V
 InnerClasses:
+  public static #x= #x of #x;          // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   public static #x= #x of #x;          // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
-  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+  public static final #x= #x of #x;   // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
   Compiled from "TinyFrameworkNative.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -3198,7 +3269,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 +3300,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 +3329,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 +3365,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..d493ad1 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
@@ -2002,6 +2002,51 @@
       #x ()Ljava/lang/Integer;
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+  Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+  minor version: 0
+  major version: 65
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 1, attributes: 4
+Constant pool:
+{
+  public int i;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iload_1
+         x: putfield      #x                 // Field i:I
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+            0      10     1     i   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;             // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
   Compiled from "TinyFrameworkMethodCallReplace.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -2010,7 +2055,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 4
+  interfaces: 0, fields: 0, methods: 4, attributes: 4
 Constant pool:
 {
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
@@ -2065,9 +2110,30 @@
     RuntimeVisibleAnnotations:
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+    descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+         x: dup
+         x: iload_0
+         x: iconst_1
+         x: iadd
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      11     0     i   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 }
 InnerClasses:
-  public static #x= #x of #x;             // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+  public static #x= #x of #x;           // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+  public static #x= #x of #x;            // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
 SourceFile: "TinyFrameworkMethodCallReplace.java"
 RuntimeVisibleAnnotations:
   x: #x()
@@ -2081,7 +2147,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 4, attributes: 6
+  interfaces: 0, fields: 0, methods: 6, attributes: 6
 Constant pool:
 {
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
@@ -2148,6 +2214,48 @@
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+    descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+         x: dup
+         x: iload_0
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester:(I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+         x: swap
+         x: pop
+        x: swap
+        x: pop
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      13     0     i   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static int originalAdd(int, int);
+    descriptor: (II)I
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_0
+         x: iload_1
+         x: iadd
+         x: iconst_1
+         x: isub
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       6     0     a   I
+            0       6     1     b   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
   private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
     descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
     flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
@@ -2167,6 +2275,7 @@
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 }
 InnerClasses:
+  public static #x= #x of #x;           // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   public static #x= #x of #x;            // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
 SourceFile: "TinyFrameworkMethodCallReplace.java"
@@ -2184,6 +2293,7 @@
       #x ()V
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
   Compiled from "TinyFrameworkNative.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -3392,7 +3502,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 +3539,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 +3996,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 +4020,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 +4037,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..8978a7a 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
@@ -2413,6 +2413,66 @@
       #x ()Ljava/lang/Integer;
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+  Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+  minor version: 0
+  major version: 65
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+Constant pool:
+{
+  public int i;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+         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
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String (I)V
+         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: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: aload_0
+        x: iload_1
+        x: putfield      #x                 // Field i:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+           11      10     1     i   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;           // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
   Compiled from "TinyFrameworkMethodCallReplace.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -2421,7 +2481,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 4, attributes: 4
+  interfaces: 0, fields: 0, methods: 5, attributes: 4
 Constant pool:
 {
   private static {};
@@ -2501,8 +2561,34 @@
     RuntimeVisibleAnnotations:
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+    descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+         x: ldc           #x                 // String newConstructorTester
+         x: ldc           #x                 // String (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+         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: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+        x: dup
+        x: iload_0
+        x: iconst_1
+        x: iadd
+        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      11     0     i   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 }
 InnerClasses:
+  public static #x= #x of #x;          // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   public static #x= #x of #x;           // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
 SourceFile: "TinyFrameworkMethodCallReplace.java"
 RuntimeVisibleAnnotations:
@@ -2517,7 +2603,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 5, attributes: 6
+  interfaces: 0, fields: 0, methods: 7, attributes: 6
 Constant pool:
 {
   private static {};
@@ -2609,18 +2695,70 @@
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+    descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+         x: ldc           #x                 // String constructorReplaceTester
+         x: ldc           #x                // String (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+         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: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+        x: dup
+        x: iload_0
+        x: invokestatic  #x                // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester:(I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+        x: swap
+        x: pop
+        x: swap
+        x: pop
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      13     0     i   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static int originalAdd(int, int);
+    descriptor: (II)I
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+         x: ldc           #x                // String originalAdd
+         x: ldc           #x                // String (II)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: iload_1
+        x: iadd
+        x: iconst_1
+        x: isub
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       6     0     a   I
+           11       6     1     b   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
   private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
     descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
     flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
     Code:
       stack=4, locals=1, args_size=1
          x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
-         x: ldc           #x                 // String lambda$nonStaticMethodCallReplaceTester$0
-         x: ldc           #x                 // String (Ljava/util/concurrent/atomic/AtomicBoolean;)V
+         x: ldc           #x                // String lambda$nonStaticMethodCallReplaceTester$0
+         x: ldc           #x                // String (Ljava/util/concurrent/atomic/AtomicBoolean;)V
          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: aload_0
-        x: invokestatic  #x                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
+        x: invokestatic  #x                // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
         x: invokevirtual #x                // Method java/lang/Thread.isDaemon:()Z
         x: invokevirtual #x                // Method java/util/concurrent/atomic/AtomicBoolean.set:(Z)V
         x: return
@@ -2633,6 +2771,7 @@
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 }
 InnerClasses:
+  public static #x= #x of #x;           // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   public static #x= #x of #x;           // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
 SourceFile: "TinyFrameworkMethodCallReplace.java"
@@ -2650,6 +2789,7 @@
       #x ()V
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
   Compiled from "TinyFrameworkNative.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -4241,7 +4381,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 +4438,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 +5111,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 +5126,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 +5136,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 +5150,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 +5161,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 +5172,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..406c611 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
@@ -1883,6 +1883,42 @@
 InnerClasses:
   public static #x= #x of #x;          // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
   public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+  Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
+  public int i;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iload_1
+         x: putfield      #x                  // Field i:I
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+            0      10     1     i   I
+}
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+InnerClasses:
+  public static #x= #x of #x;           // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
   Compiled from "TinyFrameworkMethodCallReplace.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -1891,7 +1927,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 3
+  interfaces: 0, fields: 0, methods: 4, attributes: 3
 Constant pool:
 {
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
@@ -1937,10 +1973,28 @@
         Start  Length  Slot  Name   Signature
             0       4     0     a   I
             0       4     1     b   I
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+    descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+         x: dup
+         x: iload_0
+         x: iconst_1
+         x: iadd
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      11     0     i   I
 }
 SourceFile: "TinyFrameworkMethodCallReplace.java"
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
 InnerClasses:
+  public static #x= #x of #x;          // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   public static #x= #x of #x;          // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class
   Compiled from "TinyFrameworkMethodCallReplace.java"
@@ -1950,7 +2004,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 5, attributes: 5
+  interfaces: 0, fields: 0, methods: 6, attributes: 5
 Constant pool:
 {
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
@@ -2008,6 +2062,21 @@
          x: ireturn
       LineNumberTable:
 
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+    descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+         x: dup
+         x: iload_0
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       9     0     i   I
+
   private static int originalAdd(int, int);
     descriptor: (II)I
     flags: (0x000a) ACC_PRIVATE, ACC_STATIC
@@ -2046,6 +2115,7 @@
     android.hosttest.annotation.HostSideTestWholeClassKeep
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
 BootstrapMethods:
   x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
     Method arguments:
@@ -2053,8 +2123,9 @@
       #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.lambda$nonStaticMethodCallReplaceTester$0:(Ljava/util/concurrent/atomic/AtomicBoolean;)V
       #x ()V
 InnerClasses:
+  public static #x= #x of #x;          // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   public static #x= #x of #x;          // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
-  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+  public static final #x= #x of #x;   // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
   Compiled from "TinyFrameworkNative.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -3219,7 +3290,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 +3321,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 +3350,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 +3386,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..6a8e488 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
@@ -2002,6 +2002,51 @@
       #x ()Ljava/lang/Integer;
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+  Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 1, attributes: 4
+Constant pool:
+{
+  public int i;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iload_1
+         x: putfield      #x                 // Field i:I
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+            0      10     1     i   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;             // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
   Compiled from "TinyFrameworkMethodCallReplace.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -2010,7 +2055,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 4
+  interfaces: 0, fields: 0, methods: 4, attributes: 4
 Constant pool:
 {
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
@@ -2065,9 +2110,30 @@
     RuntimeVisibleAnnotations:
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+    descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+         x: dup
+         x: iload_0
+         x: iconst_1
+         x: iadd
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      11     0     i   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 }
 InnerClasses:
-  public static #x= #x of #x;             // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+  public static #x= #x of #x;           // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+  public static #x= #x of #x;            // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
 SourceFile: "TinyFrameworkMethodCallReplace.java"
 RuntimeVisibleAnnotations:
   x: #x()
@@ -2081,7 +2147,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 4, attributes: 6
+  interfaces: 0, fields: 0, methods: 6, attributes: 6
 Constant pool:
 {
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
@@ -2148,6 +2214,48 @@
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+    descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+         x: dup
+         x: iload_0
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester:(I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+         x: swap
+         x: pop
+        x: swap
+        x: pop
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      13     0     i   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static int originalAdd(int, int);
+    descriptor: (II)I
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_0
+         x: iload_1
+         x: iadd
+         x: iconst_1
+         x: isub
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       6     0     a   I
+            0       6     1     b   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
   private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
     descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
     flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
@@ -2167,6 +2275,7 @@
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 }
 InnerClasses:
+  public static #x= #x of #x;           // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   public static #x= #x of #x;            // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
 SourceFile: "TinyFrameworkMethodCallReplace.java"
@@ -2184,6 +2293,7 @@
       #x ()V
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
   Compiled from "TinyFrameworkNative.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -3422,7 +3532,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 +3569,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 +4026,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 +4050,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 +4067,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..d8e7632 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
@@ -2413,6 +2413,66 @@
       #x ()Ljava/lang/Integer;
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+  Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+Constant pool:
+{
+  public int i;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+         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
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String (I)V
+         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: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: aload_0
+        x: iload_1
+        x: putfield      #x                 // Field i:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+           11      10     1     i   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;           // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
   Compiled from "TinyFrameworkMethodCallReplace.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -2421,7 +2481,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 4, attributes: 4
+  interfaces: 0, fields: 0, methods: 5, attributes: 4
 Constant pool:
 {
   private static {};
@@ -2501,8 +2561,34 @@
     RuntimeVisibleAnnotations:
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+    descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+         x: ldc           #x                 // String newConstructorTester
+         x: ldc           #x                 // String (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+         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: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+        x: dup
+        x: iload_0
+        x: iconst_1
+        x: iadd
+        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      11     0     i   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 }
 InnerClasses:
+  public static #x= #x of #x;          // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   public static #x= #x of #x;           // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
 SourceFile: "TinyFrameworkMethodCallReplace.java"
 RuntimeVisibleAnnotations:
@@ -2517,7 +2603,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 5, attributes: 6
+  interfaces: 0, fields: 0, methods: 7, attributes: 6
 Constant pool:
 {
   private static {};
@@ -2609,18 +2695,70 @@
       x: #x()
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+    descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+         x: ldc           #x                 // String constructorReplaceTester
+         x: ldc           #x                // String (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+         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: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+        x: dup
+        x: iload_0
+        x: invokestatic  #x                // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester:(I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+        x: swap
+        x: pop
+        x: swap
+        x: pop
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      13     0     i   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static int originalAdd(int, int);
+    descriptor: (II)I
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+         x: ldc           #x                // String originalAdd
+         x: ldc           #x                // String (II)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: iload_1
+        x: iadd
+        x: iconst_1
+        x: isub
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       6     0     a   I
+           11       6     1     b   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
   private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
     descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
     flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
     Code:
       stack=4, locals=1, args_size=1
          x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
-         x: ldc           #x                 // String lambda$nonStaticMethodCallReplaceTester$0
-         x: ldc           #x                 // String (Ljava/util/concurrent/atomic/AtomicBoolean;)V
+         x: ldc           #x                // String lambda$nonStaticMethodCallReplaceTester$0
+         x: ldc           #x                // String (Ljava/util/concurrent/atomic/AtomicBoolean;)V
          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: aload_0
-        x: invokestatic  #x                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
+        x: invokestatic  #x                // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
         x: invokevirtual #x                // Method java/lang/Thread.isDaemon:()Z
         x: invokevirtual #x                // Method java/util/concurrent/atomic/AtomicBoolean.set:(Z)V
         x: return
@@ -2633,6 +2771,7 @@
         com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 }
 InnerClasses:
+  public static #x= #x of #x;           // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   public static #x= #x of #x;           // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
 SourceFile: "TinyFrameworkMethodCallReplace.java"
@@ -2650,6 +2789,7 @@
       #x ()V
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
   Compiled from "TinyFrameworkNative.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -4271,7 +4411,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 +4468,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 +5141,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 +5156,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 +5166,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 +5180,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 +5191,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 +5202,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/policy-override-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index 1f64a3c..cbaad2e 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -68,6 +68,10 @@
 class java.lang.Thread keep
   method start ()V @com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo.startThread
 
+# Used to test constructor replacement.
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester keepclass
+  method <init> (I)V @com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester
+
 
 # "rename" takes a type internal name, so '/'s is used as a separator.
 # The leading / in the prefix is not needed (it'll be stripped), but it's added to make
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
index 57c69a3..d850be8 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
@@ -41,10 +41,23 @@
         return originalAdd(1, 2);
     }
 
+    public static ConstructorTester constructorReplaceTester(int i) {
+        // This object construction will be replaced with ReplaceTo.newConstructorTester().
+        return new ConstructorTester(i);
+    }
+
     private static int originalAdd(int a, int b) {
         return a + b - 1; // Original is broken.
     }
 
+    public static class ConstructorTester {
+        public int i;
+
+        public ConstructorTester(int i) {
+            this.i = i;
+        }
+    }
+
     public static class ReplaceTo {
         public static void startThread(Thread thread) {
             thread.setDaemon(true);
@@ -54,5 +67,9 @@
         public static int add(int a, int b) {
             return a + b;
         }
+
+        public static ConstructorTester newConstructorTester(int i) {
+            return new ConstructorTester(i + 1);
+        }
     }
 }
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..89fcd30 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);
@@ -318,4 +323,10 @@
         assertThat(TinyFrameworkMethodCallReplace.staticMethodCallReplaceTester())
                 .isEqualTo(3);
     }
+
+    @Test
+    public void testConstructorCallReplace() throws Exception {
+        assertThat(TinyFrameworkMethodCallReplace.constructorReplaceTester(5).i)
+                .isEqualTo(6);
+    }
 }
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
index b6089ea..a7f481a 100644
--- a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
@@ -449,7 +449,6 @@
             methodName: String,
             methodDesc: String,
             replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
-            policy: FilterPolicyWithReason
         ) {
             // This can't be converted to an annotation.
             classHasMember = 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 fdadaaf..5395d2a 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2625,6 +2625,13 @@
                     }
                     notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
                     r.foregroundNoti = notification;
+                    if (r.isForeground && foregroundServiceType != previousFgsType) {
+                        // An already foreground service is being started with a different fgs type
+                        // which results in the type changing without typical startForeground
+                        // logging.
+                        Slog.w(TAG_SERVICE, "FGS type change for " + r.shortInstanceName
+                                + " from " + previousFgsType + " to " + foregroundServiceType);
+                    }
                     mAm.mProcessStateController.setForegroundServiceType(r, foregroundServiceType);
                     if (!r.isForeground) {
                         final ServiceMap smap = getServiceMapLocked(r.userId);
@@ -9191,7 +9198,9 @@
         } else {
             synchronized (mAm.mPidsSelfLocked) {
                 callerApp = mAm.mPidsSelfLocked.get(callingPid);
-                caller = callerApp.getThread();
+                if (callerApp != null) {
+                    caller = callerApp.getThread();
+                }
             }
         }
         if (callerApp == null) {
@@ -9358,10 +9367,17 @@
                                             + "for package "
                                             + packageName);
                         }
-                        setServiceForegroundInnerLocked(sr, sr.foregroundId,
-                                sr.foregroundNoti, /* flags */ 0,
-                                ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
-                                /* callingUidStart */ 0, /* systemRequestedTransition */ true);
+                        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 {
                         if (DEBUG_FOREGROUND_SERVICE) {
                             Slog.d(TAG,
@@ -9411,10 +9427,17 @@
                                             + "for package "
                                             + packageName);
                         }
-                        setServiceForegroundInnerLocked(sr, /* id */ 0,
-                                /* notification */ null, /* flags */ 0,
-                                /* foregroundServiceType */ 0, /* callingUidStart */ 0,
-                                /* systemRequestedTransition */ true);
+                        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"
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/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index c8b8909..f7d7ed5 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3006,7 +3006,7 @@
             UidState uidState = getUidStateLocked(uid, false);
             if (uidState != null) {
                 int rawUidMode = mAppOpsCheckingService.getUidMode(
-                        uidState.uid, getPersistentId(virtualDeviceId), code);
+                        uidState.uid, getPersistentDeviceIdForOp(virtualDeviceId, code), code);
 
                 if (rawUidMode != AppOpsManager.opToDefaultMode(code)) {
                     return raw ? rawUidMode :
@@ -3069,7 +3069,7 @@
 
             int switchCode = AppOpsManager.opToSwitch(code);
             int rawUidMode = mAppOpsCheckingService.getUidMode(uid,
-                    getPersistentId(virtualDeviceId), switchCode);
+                    getPersistentDeviceIdForOp(virtualDeviceId, switchCode), switchCode);
 
             if (rawUidMode != AppOpsManager.opToDefaultMode(switchCode)) {
                 return raw ? rawUidMode : evaluateForegroundMode(uid, switchCode, rawUidMode);
@@ -3396,7 +3396,7 @@
             }
             final Op op = getOpLocked(ops, code, uid, true);
             final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag,
-                    getPersistentId(virtualDeviceId));
+                    getPersistentDeviceIdForOp(virtualDeviceId, code));
             if (attributedOp.isRunning()) {
                 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code "
                         + code + " startTime of in progress event="
@@ -3418,15 +3418,15 @@
 
                 // If there is a non-default per UID policy (we set UID op mode only if
                 // non-default) it takes over, otherwise use the per package policy.
-            } else if (mAppOpsCheckingService.getUidMode(
-                            uidState.uid, getPersistentId(virtualDeviceId), switchCode)
+            } else if (mAppOpsCheckingService.getUidMode(uidState.uid,
+                    getPersistentDeviceIdForOp(virtualDeviceId, switchCode), switchCode)
                     != AppOpsManager.opToDefaultMode(switchCode)) {
                 final int uidMode =
                         uidState.evalMode(
                                 code,
                                 mAppOpsCheckingService.getUidMode(
                                         uidState.uid,
-                                        getPersistentId(virtualDeviceId),
+                                        getPersistentDeviceIdForOp(virtualDeviceId, switchCode),
                                         switchCode));
                 if (uidMode != AppOpsManager.MODE_ALLOWED) {
                     if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
@@ -3478,7 +3478,8 @@
                     virtualDeviceId, flags, AppOpsManager.MODE_ALLOWED);
 
             attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag,
-                    getPersistentId(proxyVirtualDeviceId), uidState.getState(), flags, notedCount);
+                    getPersistentDeviceIdForOp(proxyVirtualDeviceId, code), uidState.getState(),
+                    flags, notedCount);
 
             if (shouldCollectAsyncNotedOp) {
                 collectAsyncNotedOp(uid, packageName, code, attributionTag, flags, message,
@@ -4045,7 +4046,7 @@
             }
             final Op op = getOpLocked(ops, code, uid, true);
             final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag,
-                    getPersistentId(virtualDeviceId));
+                    getPersistentDeviceIdForOp(virtualDeviceId, code));
             final UidState uidState = ops.uidState;
             isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag,
                     virtualDeviceId, pvr.bypass, false);
@@ -4058,8 +4059,9 @@
                 // If there is a non-default per UID policy (we set UID op mode only if
                 // non-default) it takes over, otherwise use the per package policy.
             } else if ((rawUidMode =
-                            mAppOpsCheckingService.getUidMode(
-                                    uidState.uid, getPersistentId(virtualDeviceId), switchCode))
+                    mAppOpsCheckingService.getUidMode(
+                            uidState.uid, getPersistentDeviceIdForOp(virtualDeviceId, switchCode),
+                            switchCode))
                     != AppOpsManager.opToDefaultMode(switchCode)) {
                 final int uidMode = uidState.evalMode(code, rawUidMode);
                 if (!shouldStartForMode(uidMode, startIfModeDefault)) {
@@ -4107,11 +4109,13 @@
             try {
                 if (isRestricted) {
                     attributedOp.createPaused(clientId, virtualDeviceId, proxyUid, proxyPackageName,
-                            proxyAttributionTag, getPersistentId(proxyVirtualDeviceId),
+                            proxyAttributionTag,
+                            getPersistentDeviceIdForOp(proxyVirtualDeviceId, code),
                             uidState.getState(), flags, attributionFlags, attributionChainId);
                 } else {
                     attributedOp.started(clientId, virtualDeviceId, proxyUid, proxyPackageName,
-                            proxyAttributionTag, getPersistentId(proxyVirtualDeviceId),
+                            proxyAttributionTag,
+                            getPersistentDeviceIdForOp(proxyVirtualDeviceId, code),
                             uidState.getState(), flags, attributionFlags, attributionChainId);
                     startType = START_TYPE_STARTED;
                 }
@@ -4179,15 +4183,15 @@
             final int switchCode = AppOpsManager.opToSwitch(code);
             // If there is a non-default mode per UID policy (we set UID op mode only if
             // non-default) it takes over, otherwise use the per package policy.
-            if (mAppOpsCheckingService.getUidMode(
-                            uidState.uid, getPersistentId(virtualDeviceId), switchCode)
+            if (mAppOpsCheckingService.getUidMode(uidState.uid,
+                    getPersistentDeviceIdForOp(virtualDeviceId, switchCode), switchCode)
                     != AppOpsManager.opToDefaultMode(switchCode)) {
                 final int uidMode =
                         uidState.evalMode(
                                 code,
                                 mAppOpsCheckingService.getUidMode(
                                         uidState.uid,
-                                        getPersistentId(virtualDeviceId),
+                                        getPersistentDeviceIdForOp(virtualDeviceId, switchCode),
                                         switchCode));
                 if (!shouldStartForMode(uidMode, startIfModeDefault)) {
                     if (DEBUG) {
@@ -4350,7 +4354,8 @@
                 return;
             }
             final AttributedOp attributedOp =
-                    op.mDeviceAttributedOps.getOrDefault(getPersistentId(virtualDeviceId),
+                    op.mDeviceAttributedOps.getOrDefault(
+                            getPersistentDeviceIdForOp(virtualDeviceId, code),
                             new ArrayMap<>()).get(attributionTag);
             if (attributedOp == null) {
                 Slog.e(TAG, "Attribution not found: uid=" + uid + " pkg=" + packageName + "("
@@ -4641,7 +4646,8 @@
             return true;
         }
         if (mVirtualDeviceManagerInternal == null) {
-            return true;
+            Slog.w(TAG, "VirtualDeviceManagerInternal is null when device Id is non-default");
+            return false;
         }
         if (mVirtualDeviceManagerInternal.isValidVirtualDeviceId(virtualDeviceId)) {
             mKnownDeviceIds.put(virtualDeviceId,
@@ -7310,7 +7316,13 @@
         return packageNames;
     }
 
-    @NonNull private String getPersistentId(int virtualDeviceId) {
+    // For ops associated with device aware permissions, if virtual device id is non-default, we
+    // will return string version of that id provided the virtual device has corresponding camera or
+    // audio policy.
+    @NonNull private String getPersistentDeviceIdForOp(int virtualDeviceId, int op) {
+        virtualDeviceId = PermissionManager.resolveDeviceIdForPermissionCheck(mContext,
+                virtualDeviceId, AppOpsManager.opToPermission(op));
+
         if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
             return PERSISTENT_DEVICE_ID_DEFAULT;
         }
@@ -7319,6 +7331,7 @@
         }
         String persistentId =
                 mVirtualDeviceManagerInternal.getPersistentIdForDevice(virtualDeviceId);
+
         if (persistentId == null) {
             persistentId = mKnownDeviceIds.get(virtualDeviceId);
         }
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/display/mode/ModeChangeObserver.java b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
index bbc13cc..2751835 100644
--- a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
+++ b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
@@ -23,8 +23,6 @@
 import android.view.DisplayAddress;
 import android.view.DisplayEventReceiver;
 
-import com.android.internal.annotations.KeepForWeakReference;
-
 import java.util.HashSet;
 import java.util.Set;
 
@@ -35,7 +33,6 @@
     private final DisplayModeDirector.Injector mInjector;
 
     @SuppressWarnings("unused")
-    @KeepForWeakReference
     private DisplayEventReceiver mModeChangeListener;
     private final SparseArray<Set<Integer>> mRejectedModesByDisplay = new SparseArray<>();
     private Looper mLooper;
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/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index f96846c..acdc79f 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1067,6 +1067,9 @@
     void doPostDexopt(List<ReconciledPackage> reconciledPackages,
             List<InstallRequest> requests, Map<String, Boolean> createdAppId,
             MoveInfo moveInfo, long acquireTime) {
+        for (InstallRequest request : requests) {
+            request.onWaitDexoptFinished();
+        }
         boolean success = false;
         try {
             if (commitInstallPackages(reconciledPackages)) {
@@ -1218,6 +1221,7 @@
             CompletableFuture<Void> future =
                     DexOptHelper.performDexoptIfNeededAsync(request, mDexManager);
             completableFutures.add(future);
+            request.onWaitDexoptStarted();
         }
 
         if (!completableFutures.isEmpty()) {
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index fbf5db5..7349204 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -1028,6 +1028,18 @@
         }
     }
 
+    public void onWaitDexoptStarted() {
+        if (mPackageMetrics != null) {
+            mPackageMetrics.onStepStarted(PackageMetrics.STEP_WAIT_DEXOPT);
+        }
+    }
+
+    public void onWaitDexoptFinished() {
+        if (mPackageMetrics != null) {
+            mPackageMetrics.onStepFinished(PackageMetrics.STEP_WAIT_DEXOPT);
+        }
+    }
+
     public void onDexoptFinished(DexoptResult dexoptResult) {
         // Only report external profile warnings when installing from adb. The goal is to warn app
         // developers if they have provided bad external profiles, so it's not beneficial to report
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/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index 994ee42..22da3b2 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -73,6 +73,7 @@
     public static final int STEP_DEXOPT = 5;
     public static final int STEP_FREEZE_INSTALL = 6;
     public static final int STEP_RESTORE = 7;
+    public static final int STEP_WAIT_DEXOPT = 8;
 
     @IntDef(prefix = {"STEP_"}, value = {
             STEP_PREPARE,
@@ -81,7 +82,8 @@
             STEP_COMMIT,
             STEP_DEXOPT,
             STEP_FREEZE_INSTALL,
-            STEP_RESTORE
+            STEP_RESTORE,
+            STEP_WAIT_DEXOPT
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface StepInt {
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/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 12f5534..58534b9 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -712,13 +712,17 @@
             if (!isMagnifierActivated) {
                 return;
             }
-            // All opening/closing situations.
+            // All opening/closing/recents transitions
+            boolean notify = (flags & TRANSIT_FLAG_IS_RECENTS) != 0;
             switch (type) {
                 case WindowManager.TRANSIT_OPEN:
                 case WindowManager.TRANSIT_TO_FRONT:
                 case WindowManager.TRANSIT_CLOSE:
                 case WindowManager.TRANSIT_TO_BACK:
-                    mUserContextChangedNotifier.onWMTransition(type, flags);
+                    notify = true;
+            }
+            if (notify) {
+                mUserContextChangedNotifier.onWMTransition(type, flags);
             }
         }
 
@@ -1088,8 +1092,7 @@
             // causing the notifying, or the recents/home window is removed, then we won't need the
             // delayed notification anymore.
             void onWMTransition(@TransitionType int type, @TransitionFlags int flags) {
-                if (type == WindowManager.TRANSIT_TO_FRONT
-                        && (flags & TRANSIT_FLAG_IS_RECENTS) != 0) {
+                if ((flags & TRANSIT_FLAG_IS_RECENTS) != 0) {
                     // Delay the recents to front transition notification then send after if needed.
                     mHasDelayedNotificationForRecentsToFrontTransition = true;
                 } else {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 247264f..bdde5fe 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -58,6 +58,7 @@
 import static android.security.Flags.preventIntentRedirectAbortOrThrowException;
 import static android.security.Flags.preventIntentRedirectShowToast;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_FLAG_AVOID_MOVE_TO_FRONT;
 import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -1841,6 +1842,9 @@
             // no-user-leaving implies not entering PiP.
             transition.setCanPipOnFinish(false /* canPipOnFinish */);
         }
+        if (avoidMoveToFront() && transition != null) {
+            transition.addFlag(TRANSIT_FLAG_AVOID_MOVE_TO_FRONT);
+        }
         if (isIndependentLaunch && transition != null) {
             transitionController.requestStartTransition(transition,
                     mTargetTask == null ? started.getTask() : mTargetTask,
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/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index d291d99..98e453b 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -25,6 +25,7 @@
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
 import android.os.Build;
+import android.os.DeadObjectException;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.Trace;
@@ -66,64 +67,80 @@
     /**
      * Schedules a transaction, which may consist of multiple callbacks and a lifecycle request.
      * @param transaction A sequence of client transaction items.
-     * @throws RemoteException
-     *
+     * @return {@code false} if the transaction failed because of {@link RemoteException}.
      * @see ClientTransaction
      */
     @VisibleForTesting
-    void scheduleTransaction(@NonNull ClientTransaction transaction) throws RemoteException {
-        final IApplicationThread client = transaction.getClient();
-        try {
-            transaction.schedule();
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Failed to deliver transaction for " + client
-                            + "\ntransaction=" + transaction);
-            throw e;
+    boolean scheduleTransaction(@NonNull ClientTransaction transaction) {
+        final RemoteException e = transaction.schedule();
+        if (e != null) {
+            final WindowProcessController wpc = mWms.mAtmService.getProcessController(
+                    transaction.getClient());
+            Slog.w(TAG, "Failed to deliver transaction for " + wpc + "\ntransaction=" + this, e);
+            return false;
         }
+        return true;
     }
 
     /**
      * Similar to {@link #scheduleTransactionItem}, but it sends the transaction immediately and
      * it can be called without WM lock.
      *
+     * @return {@code false} if the transaction failed because of {@link RemoteException}.
      * @see WindowProcessController#setReportedProcState(int)
      */
-    void scheduleTransactionItemNow(@NonNull IApplicationThread client,
+    boolean scheduleTransactionItemNow(@NonNull IApplicationThread client,
             @NonNull ClientTransactionItem transactionItem) throws RemoteException {
         final ClientTransaction clientTransaction = new ClientTransaction(client);
         clientTransaction.addTransactionItem(transactionItem);
-        scheduleTransaction(clientTransaction);
+        final boolean res = scheduleTransaction(clientTransaction);
+        if (!com.android.window.flags.Flags.cleanupDispatchPendingTransactionsRemoteException()
+                && !res) {
+            throw new DeadObjectException();
+        }
+        return res;
     }
 
     /**
-     * Schedules a transaction with the given item, delivery to client application.
+     * Schedules a transaction with the given item, delivery to client application, which may be
+     * queued to dispatched later.
      *
-     * @throws RemoteException
+     * @return {@code false} if the transaction was dispatched immediately, but failed because of
+     *         {@link RemoteException}. If the transaction is queued, any failure will be ignored.
      * @see ClientTransactionItem
      */
-    void scheduleTransactionItem(@NonNull IApplicationThread client,
+    boolean scheduleTransactionItem(@NonNull IApplicationThread client,
             @NonNull ClientTransactionItem item) throws RemoteException {
         // Wait until RootWindowContainer#performSurfacePlacementNoTrace to dispatch all pending
         // transactions at once.
         final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
         clientTransaction.addTransactionItem(item);
 
-        onClientTransactionItemScheduled(clientTransaction, false /* shouldDispatchImmediately */);
+        final boolean res = onClientTransactionItemScheduled(clientTransaction,
+                false /* shouldDispatchImmediately */);
+        if (!com.android.window.flags.Flags.cleanupDispatchPendingTransactionsRemoteException()
+                && !res) {
+            throw new DeadObjectException();
+        }
+        return res;
     }
 
     /**
-     * Schedules a transaction with the given items, delivery to client application.
+     * Schedules a transaction with the given items, delivery to client application, which may be
+     * queued to dispatched later.
      *
-     * @throws RemoteException
+     * @return {@code false} if the transaction was dispatched immediately, but failed because of
+     *         {@link RemoteException}. If the transaction is queued, any failure will be ignored.
      * @see ClientTransactionItem
      */
-    void scheduleTransactionItems(@NonNull IApplicationThread client,
+    boolean scheduleTransactionItems(@NonNull IApplicationThread client,
             @NonNull ClientTransactionItem... items) throws RemoteException {
-        scheduleTransactionItems(client, false /* shouldDispatchImmediately */, items);
+        return scheduleTransactionItems(client, false /* shouldDispatchImmediately */, items);
     }
 
     /**
-     * Schedules a transaction with the given items, delivery to client application.
+     * Schedules a transaction with the given items, delivery to client application, which may be
+     * queued to dispatched later.
      *
      * @param shouldDispatchImmediately whether or not to dispatch the transaction immediately. This
      *                                  should only be {@code true} when it is important to know the
@@ -131,11 +148,11 @@
      *                                  launches an app, the server needs to know if the transaction
      *                                  is dispatched successfully, and may restart the process if
      *                                  not.
-     *
-     * @throws RemoteException
+     * @return {@code false} if the transaction was dispatched immediately, but failed because of
+     *         {@link RemoteException}. If the transaction is queued, any failure will be ignored.
      * @see ClientTransactionItem
      */
-    void scheduleTransactionItems(@NonNull IApplicationThread client,
+    boolean scheduleTransactionItems(@NonNull IApplicationThread client,
             boolean shouldDispatchImmediately,
             @NonNull ClientTransactionItem... items) throws RemoteException {
         // Wait until RootWindowContainer#performSurfacePlacementNoTrace to dispatch all pending
@@ -147,7 +164,13 @@
             clientTransaction.addTransactionItem(items[i]);
         }
 
-        onClientTransactionItemScheduled(clientTransaction, shouldDispatchImmediately);
+        final boolean res = onClientTransactionItemScheduled(clientTransaction,
+                shouldDispatchImmediately);
+        if (!com.android.window.flags.Flags.cleanupDispatchPendingTransactionsRemoteException()
+                && !res) {
+            throw new DeadObjectException();
+        }
+        return res;
     }
 
     /** Executes all the pending transactions. */
@@ -159,12 +182,7 @@
         final int size = mPendingTransactions.size();
         for (int i = 0; i < size; i++) {
             final ClientTransaction transaction = mPendingTransactions.valueAt(i);
-            try {
-                scheduleTransaction(transaction);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to deliver pending transaction", e);
-                // TODO(b/323801078): apply cleanup for individual transaction item if needed.
-            }
+            scheduleTransaction(transaction);
         }
         mPendingTransactions.clear();
         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
@@ -174,12 +192,7 @@
     void dispatchPendingTransaction(@NonNull IApplicationThread client) {
         final ClientTransaction pendingTransaction = mPendingTransactions.remove(client.asBinder());
         if (pendingTransaction != null) {
-            try {
-                scheduleTransaction(pendingTransaction);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to deliver pending transaction", e);
-                // TODO(b/323801078): apply cleanup for individual transaction item if needed.
-            }
+            scheduleTransaction(pendingTransaction);
         }
     }
 
@@ -211,15 +224,22 @@
         return transaction;
     }
 
-    /** Must only be called with WM lock. */
-    private void onClientTransactionItemScheduled(
+    /**
+     * Must only be called with WM lock.
+     * If the transaction should not be queued, it will be dispatched immediately.
+     *
+     * @return {@code false} if the transaction was dispatched immediately, but failed because of
+     *         {@link RemoteException}.
+     */
+    private boolean onClientTransactionItemScheduled(
             @NonNull ClientTransaction clientTransaction,
-            boolean shouldDispatchImmediately) throws RemoteException {
+            boolean shouldDispatchImmediately) {
         if (shouldDispatchImmediately || shouldDispatchPendingTransactionsImmediately()) {
             // Dispatch the pending transaction immediately.
             mPendingTransactions.remove(clientTransaction.getClient().asBinder());
-            scheduleTransaction(clientTransaction);
+            return scheduleTransaction(clientTransaction);
         }
+        return true;
     }
 
     /** Must only be called with WM lock. */
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/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/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/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 301a754..9d4fe9e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -41,8 +41,8 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.pm.ApplicationInfo.CATEGORY_SOCIAL;
 import static android.content.pm.ApplicationInfo.CATEGORY_GAME;
+import static android.content.pm.ApplicationInfo.CATEGORY_SOCIAL;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.content.res.Configuration.UI_MODE_TYPE_DESK;
@@ -66,7 +66,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -196,7 +195,7 @@
         // Because the booted state is set, avoid starting real home if there is no task.
         doReturn(false).when(mRootWindowContainer).resumeHomeActivity(any(), anyString(), any());
         // Do not execute the transaction, because we can't verify the parameter after it recycles.
-        doNothing().when(mClientLifecycleManager).scheduleTransaction(any());
+        doReturn(true).when(mClientLifecycleManager).scheduleTransaction(any());
     }
 
     private TestStartingWindowOrganizer registerTestStartingWindowOrganizer() {
@@ -266,7 +265,7 @@
                     break;
                 }
             }
-            return null;
+            return true;
         }).when(mClientLifecycleManager).scheduleTransaction(any());
 
         activity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
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/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
index 02ad9db..8d214dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
@@ -16,11 +16,13 @@
 
 package com.android.server.wm;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -32,17 +34,19 @@
 import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.ClientTransactionItem;
+import android.os.DeadObjectException;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.window.flags.Flags;
+
 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;
 
@@ -60,15 +64,11 @@
     @Mock
     private IApplicationThread mClient;
     @Mock
-    private IApplicationThread.Stub mNonBinderClient;
-    @Mock
     private ClientTransaction mTransaction;
     @Mock
     private ClientTransactionItem mTransactionItem;
     @Mock
     private ActivityLifecycleItem mLifecycleItem;
-    @Captor
-    private ArgumentCaptor<ClientTransaction> mTransactionCaptor;
 
     private WindowManagerService mWms;
     private ClientLifecycleManager mLifecycleManager;
@@ -83,7 +83,6 @@
 
         doReturn(true).when(mLifecycleItem).isActivityLifecycleItem();
         doReturn(mClientBinder).when(mClient).asBinder();
-        doReturn(mNonBinderClient).when(mNonBinderClient).asBinder();
     }
 
     @Test
@@ -91,13 +90,11 @@
         spyOn(mWms.mWindowPlacerLocked);
         doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
 
-        // Use non binder client to get non-recycled ClientTransaction.
-        mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mTransactionItem);
+        mLifecycleManager.scheduleTransactionItem(mClient, mTransactionItem);
 
         // When there is traversal scheduled, add transaction items to pending.
         assertEquals(1, mLifecycleManager.mPendingTransactions.size());
-        ClientTransaction transaction =
-                mLifecycleManager.mPendingTransactions.get(mNonBinderClient);
+        ClientTransaction transaction = mLifecycleManager.mPendingTransactions.get(mClientBinder);
         assertEquals(1, transaction.getTransactionItems().size());
         assertEquals(mTransactionItem, transaction.getTransactionItems().get(0));
         // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
@@ -108,10 +105,10 @@
 
         // Add new transaction item to the existing pending.
         clearInvocations(mLifecycleManager);
-        mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mLifecycleItem);
+        mLifecycleManager.scheduleTransactionItem(mClient, mLifecycleItem);
 
         assertEquals(1, mLifecycleManager.mPendingTransactions.size());
-        transaction = mLifecycleManager.mPendingTransactions.get(mNonBinderClient);
+        transaction = mLifecycleManager.mPendingTransactions.get(mClientBinder);
         assertEquals(2, transaction.getTransactionItems().size());
         assertEquals(mTransactionItem, transaction.getTransactionItems().get(0));
         assertEquals(mLifecycleItem, transaction.getTransactionItems().get(1));
@@ -124,8 +121,7 @@
 
     @Test
     public void testScheduleTransactionItemNow() throws RemoteException {
-        // Use non binder client to get non-recycled ClientTransaction.
-        mLifecycleManager.scheduleTransactionItemNow(mNonBinderClient, mTransactionItem);
+        mLifecycleManager.scheduleTransactionItemNow(mClient, mTransactionItem);
 
         // Dispatch immediately.
         assertTrue(mLifecycleManager.mPendingTransactions.isEmpty());
@@ -137,13 +133,11 @@
         spyOn(mWms.mWindowPlacerLocked);
         doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
 
-        // Use non binder client to get non-recycled ClientTransaction.
-        mLifecycleManager.scheduleTransactionItems(mNonBinderClient, mTransactionItem,
-                mLifecycleItem);
+        mLifecycleManager.scheduleTransactionItems(mClient, mTransactionItem, mLifecycleItem);
 
         assertEquals(1, mLifecycleManager.mPendingTransactions.size());
         final ClientTransaction transaction =
-                mLifecycleManager.mPendingTransactions.get(mNonBinderClient);
+                mLifecycleManager.mPendingTransactions.get(mClientBinder);
         assertEquals(2, transaction.getTransactionItems().size());
         assertEquals(mTransactionItem, transaction.getTransactionItems().get(0));
         assertEquals(mLifecycleItem, transaction.getTransactionItems().get(1));
@@ -160,8 +154,8 @@
         spyOn(mWms.mWindowPlacerLocked);
         doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
 
-        // Use non binder client to get non-recycled ClientTransaction.
-        mLifecycleManager.scheduleTransactionItems(mNonBinderClient,
+        mLifecycleManager.scheduleTransactionItems(
+                mClient,
                 true /* shouldDispatchImmediately */,
                 mTransactionItem, mLifecycleItem);
 
@@ -187,7 +181,7 @@
         doReturn(true).when(mWms.mWindowPlacerLocked).isLayoutDeferred();
 
         // Queue transactions during layout deferred.
-        mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mLifecycleItem);
+        mLifecycleManager.scheduleTransactionItem(mClient, mLifecycleItem);
 
         verify(mLifecycleManager, never()).scheduleTransaction(any());
 
@@ -203,4 +197,42 @@
 
         verify(mLifecycleManager).scheduleTransaction(any());
     }
+
+    @EnableFlags(Flags.FLAG_CLEANUP_DISPATCH_PENDING_TRANSACTIONS_REMOTE_EXCEPTION)
+    @Test
+    public void testOnRemoteException_returnTrueOnSuccess() throws RemoteException {
+        final boolean res = mLifecycleManager.scheduleTransactionItemNow(mClient, mTransactionItem);
+
+        assertTrue(res);
+    }
+
+    @EnableFlags(Flags.FLAG_CLEANUP_DISPATCH_PENDING_TRANSACTIONS_REMOTE_EXCEPTION)
+    @Test
+    public void testOnRemoteException_returnFalseOnFailure() throws RemoteException {
+        final DeadObjectException e = new DeadObjectException();
+        doThrow(e).when(mClient).scheduleTransaction(any());
+
+        // No exception when flag enabled.
+        final boolean res = mLifecycleManager.scheduleTransactionItemNow(mClient, mTransactionItem);
+
+        assertFalse(res);
+    }
+
+    @EnableFlags(Flags.FLAG_CLEANUP_DISPATCH_PENDING_TRANSACTIONS_REMOTE_EXCEPTION)
+    @Test
+    public void testOnRemoteException_returnTrueForQueueing() throws RemoteException {
+        spyOn(mWms.mWindowPlacerLocked);
+        doReturn(true).when(mWms.mWindowPlacerLocked).isLayoutDeferred();
+        final DeadObjectException e = new DeadObjectException();
+        doThrow(e).when(mClient).scheduleTransaction(any());
+
+        final boolean res = mLifecycleManager.scheduleTransactionItem(mClient, mTransactionItem);
+
+        assertTrue(res);
+
+        doReturn(false).when(mWms.mWindowPlacerLocked).isLayoutDeferred();
+        mLifecycleManager.onLayoutContinued();
+
+        verify(mLifecycleManager).scheduleTransaction(any());
+    }
 }
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/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index d228970..9dc7026 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -24,7 +24,6 @@
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -295,7 +294,7 @@
 
     @Test
     public void testCachedStateConfigurationChange() throws RemoteException {
-        doNothing().when(mClientLifecycleManager).scheduleTransactionItemNow(any(), any());
+        doReturn(true).when(mClientLifecycleManager).scheduleTransactionItemNow(any(), any());
         final IApplicationThread thread = mWpc.getThread();
         final Configuration newConfig = new Configuration(mWpc.getConfiguration());
         newConfig.densityDpi += 100;
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/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java b/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java
index 1bdf019..9d87fbd 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java
@@ -18,10 +18,12 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 
 import android.app.jank.Flags;
 import android.app.jank.JankTracker;
 import android.app.jank.StateTracker;
+import android.content.Context;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -55,10 +57,9 @@
      * Start an empty activity so decore view is not null when creating the JankTracker instance.
      */
     private static ActivityScenario<EmptyActivity> sEmptyActivityRule;
-
     private static String sActivityName;
-
     private static View sActivityDecorView;
+    private static Context sContext;
 
     @BeforeClass
     public static void classSetup() {
@@ -66,6 +67,7 @@
         sEmptyActivityRule.onActivity(activity -> {
             sActivityDecorView = activity.getWindow().getDecorView();
             sActivityName = activity.toString();
+            sContext = activity.getApplicationContext();
         });
     }
 
@@ -168,4 +170,14 @@
 
         assertNotNull(jankTracker);
     }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+    public void jankTracker_IsNull_WhenViewNotInHierarchy() {
+        TestWidget testWidget = new TestWidget(sContext);
+        JankTracker jankTracker = testWidget.getJankTracker();
+
+        assertNull(jankTracker);
+    }
+
 }
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/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