Merge "Show media output header when calling" into main
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index f10c0fc..eabe1f1 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -872,6 +872,7 @@
   public static final class AppOpsManager.OpEventProxyInfo implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public String getAttributionTag();
+    method @FlaggedApi("android.permission.flags.device_id_in_op_proxy_info_enabled") @Nullable public String getDeviceId();
     method @Nullable public String getPackageName();
     method @IntRange(from=0) public int getUid();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 68cc17b..caaaf51 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -7438,6 +7438,7 @@
         }
         mDdmSyncStageUpdater.next(Stage.Running);
 
+        long timestampApplicationOnCreateNs = 0;
         try {
             // If the app is being launched for full backup or restore, bring it up in
             // a restricted environment with the base application class.
@@ -7480,8 +7481,10 @@
                     + data.instrumentationName + ": " + e.toString(), e);
             }
             try {
+                timestampApplicationOnCreateNs = SystemClock.elapsedRealtimeNanos();
                 mInstrumentation.callApplicationOnCreate(app);
             } catch (Exception e) {
+                timestampApplicationOnCreateNs = 0;
                 if (!mInstrumentation.onException(app, e)) {
                     throw new RuntimeException(
                       "Unable to create application " + app.getClass().getName()
@@ -7519,7 +7522,7 @@
         }
 
         try {
-            mgr.finishAttachApplication(mStartSeq);
+            mgr.finishAttachApplication(mStartSeq, timestampApplicationOnCreateNs);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 20b2357..2d0f6fc 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -3457,6 +3457,8 @@
         private @Nullable String mPackageName;
         /** Attribution tag of the proxy that noted the op */
         private @Nullable String mAttributionTag;
+        /** Persistent device Id of the proxy that noted the op */
+        private @Nullable String mDeviceId;
 
         /**
          * Reinit existing object with new state.
@@ -3464,14 +3466,16 @@
          * @param uid UID of the proxy app that noted the op
          * @param packageName Package of the proxy that noted the op
          * @param attributionTag attribution tag of the proxy that noted the op
+         * @param deviceId Persistent device Id of the proxy that noted the op
          *
          * @hide
          */
         public void reinit(@IntRange(from = 0) int uid, @Nullable String packageName,
-                @Nullable String attributionTag) {
+                @Nullable String attributionTag, @Nullable String deviceId) {
             mUid = Preconditions.checkArgumentNonnegative(uid);
             mPackageName = packageName;
             mAttributionTag = attributionTag;
+            mDeviceId = deviceId;
         }
 
 
@@ -3505,16 +3509,33 @@
                 @IntRange(from = 0) int uid,
                 @Nullable String packageName,
                 @Nullable String attributionTag) {
+            this(uid, packageName, attributionTag,
+                    VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
+        }
+
+        /**
+         * Creates a new OpEventProxyInfo.
+         *
+         * @param uid UID of the proxy app that noted the op
+         * @param packageName Package of the proxy that noted the op
+         * @param attributionTag Attribution tag of the proxy that noted the op
+         * @param deviceId Persistent device Id of the proxy that noted the op
+         *
+         * @hide
+         */
+        public OpEventProxyInfo(
+                @IntRange(from = 0) int uid,
+                @Nullable String packageName,
+                @Nullable String attributionTag,
+                @Nullable String deviceId) {
             this.mUid = uid;
             com.android.internal.util.AnnotationValidations.validate(
                     IntRange.class, null, mUid,
                     "from", 0);
             this.mPackageName = packageName;
             this.mAttributionTag = attributionTag;
-
-            // onConstructed(); // You can define this method to get a callback
+            this.mDeviceId = deviceId;
         }
-
         /**
          * Copy constructor
          *
@@ -3525,6 +3546,7 @@
             mUid = orig.mUid;
             mPackageName = orig.mPackageName;
             mAttributionTag = orig.mAttributionTag;
+            mDeviceId = orig.mDeviceId;
         }
 
         /**
@@ -3551,6 +3573,9 @@
             return mAttributionTag;
         }
 
+        @FlaggedApi(Flags.FLAG_DEVICE_ID_IN_OP_PROXY_INFO_ENABLED)
+        public @Nullable String getDeviceId() { return mDeviceId; }
+
         @Override
         @DataClass.Generated.Member
         public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -3560,10 +3585,12 @@
             byte flg = 0;
             if (mPackageName != null) flg |= 0x2;
             if (mAttributionTag != null) flg |= 0x4;
+            if (mDeviceId != null) flg |= 0x8;
             dest.writeByte(flg);
             dest.writeInt(mUid);
             if (mPackageName != null) dest.writeString(mPackageName);
             if (mAttributionTag != null) dest.writeString(mAttributionTag);
+            if (mDeviceId != null) dest.writeString(mDeviceId);
         }
 
         @Override
@@ -3581,14 +3608,14 @@
             int uid = in.readInt();
             String packageName = (flg & 0x2) == 0 ? null : in.readString();
             String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
-
+            String deviceId = (flg & 0x8) == 0 ? null : in.readString();
             this.mUid = uid;
             com.android.internal.util.AnnotationValidations.validate(
                     IntRange.class, null, mUid,
                     "from", 0);
             this.mPackageName = packageName;
             this.mAttributionTag = attributionTag;
-
+            this.mDeviceId = deviceId;
             // onConstructed(); // You can define this method to get a callback
         }
 
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 3765c81..e8b57f2 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -196,7 +196,7 @@
     oneway void finishReceiver(in IBinder who, int resultCode, in String resultData, in Bundle map,
             boolean abortBroadcast, int flags);
     void attachApplication(in IApplicationThread app, long startSeq);
-    void finishAttachApplication(long startSeq);
+    void finishAttachApplication(long startSeq, long timestampApplicationOnCreateNs);
     List<ActivityManager.RunningTaskInfo> getTasks(int maxNum);
     @UnsupportedAppUsage
     void moveTaskToFront(in IApplicationThread caller, in String callingPackage, int task,
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 05a2aec..c3bac71 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -18,7 +18,6 @@
 
 import static android.annotation.Dimension.DP;
 import static android.app.Flags.evenlyDividedCallStyleActionLayout;
-import static android.app.Flags.updateRankingTime;
 import static android.app.admin.DevicePolicyResources.Drawables.Source.NOTIFICATION;
 import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_COLORED;
 import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
@@ -897,15 +896,16 @@
     /**
      * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
      * the notification's presence and contents in untrusted situations (namely, on the secure
-     * lockscreen).
+     * lockscreen and during screen sharing).
      *
      * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
      * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
      * shown in all situations, but the contents are only available if the device is unlocked for
-     * the appropriate user.
+     * the appropriate user and there is no active screen sharing session.
      *
      * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
-     * can be read even in an "insecure" context (that is, above a secure lockscreen).
+     * can be read even in an "insecure" context (that is, above a secure lockscreen or while
+     * screen sharing with a remote viewer).
      * To modify the public version of this notification—for example, to redact some portions—see
      * {@link Builder#setPublicVersion(Notification)}.
      *
@@ -924,7 +924,8 @@
     public @interface Visibility {}
 
     /**
-     * Notification visibility: Show this notification in its entirety on all lockscreens.
+     * Notification visibility: Show this notification in its entirety on all lockscreens and while
+     * screen sharing.
      *
      * {@see #visibility}
      */
@@ -932,14 +933,16 @@
 
     /**
      * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
-     * private information on secure lockscreens.
+     * private information on secure lockscreens. Conceal sensitive or private information while
+     * screen sharing.
      *
      * {@see #visibility}
      */
     public static final int VISIBILITY_PRIVATE = 0;
 
     /**
-     * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
+     * Notification visibility: Do not reveal any part of this notification on a secure lockscreen
+     * or while screen sharing.
      *
      * {@see #visibility}
      */
@@ -2596,7 +2599,7 @@
     public Notification()
     {
         this.when = System.currentTimeMillis();
-        if (updateRankingTime()) {
+        if (Flags.sortSectionByTime()) {
             creationTime = when;
             extras.putBoolean(EXTRA_SHOW_WHEN, true);
         } else {
@@ -2612,7 +2615,7 @@
     public Notification(Context context, int icon, CharSequence tickerText, long when,
             CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
     {
-        if (updateRankingTime()) {
+        if (Flags.sortSectionByTime()) {
             creationTime = when;
             extras.putBoolean(EXTRA_SHOW_WHEN, true);
         }
@@ -2645,7 +2648,7 @@
         this.icon = icon;
         this.tickerText = tickerText;
         this.when = when;
-        if (updateRankingTime()) {
+        if (Flags.sortSectionByTime()) {
             creationTime = when;
             extras.putBoolean(EXTRA_SHOW_WHEN, true);
         } else {
@@ -3256,8 +3259,9 @@
                 boolean mustClearCookie = false;
                 if (!parcel.hasClassCookie(Notification.class)) {
                     // This is the "root" notification, and not an "inner" notification (including
-                    // publicVersion or anything else that might be embedded in extras).
-                    parcel.setClassCookie(Notification.class, this);
+                    // publicVersion or anything else that might be embedded in extras). So we want
+                    // to use its token for every inner notification (might be null).
+                    parcel.setClassCookie(Notification.class, mAllowlistToken);
                     mustClearCookie = true;
                 }
                 try {
@@ -3266,7 +3270,7 @@
                     writeToParcelImpl(parcel, flags);
                 } finally {
                     if (mustClearCookie) {
-                        parcel.removeClassCookie(Notification.class, this);
+                        parcel.removeClassCookie(Notification.class, mAllowlistToken);
                     }
                 }
             } else {
@@ -3290,14 +3294,9 @@
         parcel.writeInt(1);
 
         if (Flags.secureAllowlistToken()) {
-            Notification rootNotification = (Notification) parcel.getClassCookie(
-                    Notification.class);
-            if (rootNotification != null && rootNotification != this) {
-                // Always use the same token as the root notification
-                parcel.writeStrongBinder(rootNotification.mAllowlistToken);
-            } else {
-                parcel.writeStrongBinder(mAllowlistToken);
-            }
+            // Always use the same token as the root notification (might be null).
+            IBinder rootNotificationToken = (IBinder) parcel.getClassCookie(Notification.class);
+            parcel.writeStrongBinder(rootNotificationToken);
         } else {
             parcel.writeStrongBinder(mAllowlistToken);
         }
@@ -5981,21 +5980,22 @@
                 }
                 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
                     contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
-                    contentView.setLong(R.id.chronometer, "setBase",
-                            mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
+                    contentView.setLong(R.id.chronometer, "setBase", mN.getWhen()
+                            + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
                     contentView.setBoolean(R.id.chronometer, "setStarted", true);
                     boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
                     contentView.setChronometerCountDown(R.id.chronometer, countsDown);
                     setTextViewColorSecondary(contentView, R.id.chronometer, p);
                 } else {
                     contentView.setViewVisibility(R.id.time, View.VISIBLE);
-                    contentView.setLong(R.id.time, "setTime", mN.when);
+                    contentView.setLong(R.id.time, "setTime", mN.getWhen());
                     setTextViewColorSecondary(contentView, R.id.time, p);
                 }
             } else {
                 // We still want a time to be set but gone, such that we can show and hide it
                 // on demand in case it's a child notification without anything in the header
-                contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
+                contentView.setLong(R.id.time, "setTime", mN.getWhen() != 0 ? mN.getWhen() :
+                        mN.creationTime);
                 setTextViewColorSecondary(contentView, R.id.time, p);
             }
         }
@@ -7162,7 +7162,7 @@
                 }
             }
 
-            if (!updateRankingTime()) {
+            if (!Flags.sortSectionByTime()) {
                 mN.creationTime = System.currentTimeMillis();
             }
 
@@ -7615,10 +7615,29 @@
     }
 
     /**
+     * Returns #when, unless it's set to 0, which should be shown as/treated as a 'current'
+     * notification. 0 is treated as a special value because it was special in an old version of
+     * android, and some apps are still (incorrectly) using it.
+     *
+     * @hide
+     */
+    public long getWhen() {
+        if (Flags.sortSectionByTime()) {
+            if (when == 0) {
+                return creationTime;
+            }
+        }
+        return when;
+    }
+
+    /**
      * @return true if the notification will show the time; false otherwise
      * @hide
      */
     public boolean showsTime() {
+        if (Flags.sortSectionByTime()) {
+            return extras.getBoolean(EXTRA_SHOW_WHEN);
+        }
         return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
     }
 
@@ -7627,6 +7646,9 @@
      * @hide
      */
     public boolean showsChronometer() {
+        if (Flags.sortSectionByTime()) {
+            return extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
+        }
         return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
     }
 
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 9b3fb5c..726064e 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -1198,17 +1198,18 @@
      * Callback called when a particular foreground service type has timed out.
      *
      * <p>This callback is meant to give the app a small grace period of a few seconds to finish
-     * the foreground service of the offending type - if it fails to do so, the app will be
+     * the foreground service of the associated type - if it fails to do so, the app will be
      * declared an ANR.
      *
-     * <p>The foreground service of the offending type can be stopped within the time limit by
+     * <p>The foreground service of the associated type can be stopped within the time limit by
      * {@link android.app.Service#stopSelf()},
      * {@link android.content.Context#stopService(android.content.Intent)} or their overloads.
      * {@link android.app.Service#stopForeground(int)} can be used as well, which demotes the
      * service to a "background" service, which will soon be stopped by the system.
      *
      * <p>The specific time limit for each type (if one exists) is mentioned in the documentation
-     * for that foreground service type.
+     * for that foreground service type. See
+     * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_DATA_SYNC dataSync} for example.
      *
      * <p>Note: time limits are restricted to a rolling 24-hour window - for example, if a
      * foreground service type has a time limit of 6 hours, that time counter begins as soon as the
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index e4425ca..8c4667f 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -40,3 +40,13 @@
      description: "Add a new callback in Service to indicate a FGS has reached its timeout."
      bug: "317799821"
 }
+
+flag {
+     namespace: "system_performance"
+     name: "app_start_info_timestamps"
+     description: "Additional timestamps."
+     bug: "287153617"
+     metadata {
+         purpose: PURPOSE_BUGFIX
+     }
+}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 7ac9547..4f06209 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1348,6 +1348,17 @@
     public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // buganizer id
 
     /**
+     * This change id restricts treatments that force a given min aspect ratio to
+     * only when an app is connected to the camera
+     *
+     * @hide
+     */
+    @ChangeId
+    @Overridable
+    @Disabled
+    public static final long OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA = 325586858L; // buganizer id
+
+    /**
      * This change id restricts treatments that force a given min aspect ratio to activities
      * whose orientation is fixed to portrait.
      *
diff --git a/core/java/android/hardware/biometrics/BiometricTestSession.java b/core/java/android/hardware/biometrics/BiometricTestSession.java
index c62680f..027d101 100644
--- a/core/java/android/hardware/biometrics/BiometricTestSession.java
+++ b/core/java/android/hardware/biometrics/BiometricTestSession.java
@@ -92,6 +92,7 @@
         mTestedUsers = new ArraySet<>();
         mUsersCleaningUp = new ArraySet<>();
         setTestHalEnabled(true);
+        Log.d(getTag(), "Opening BiometricTestSession");
     }
 
     /**
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 6b5e17d..b588308 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -180,3 +180,11 @@
     description: "Use runtime permission state to determine appop state"
     bug: "266164193"
 }
+
+flag {
+    name: "device_id_in_op_proxy_info_enabled"
+    is_fixed_read_only: true
+    namespace: "permissions"
+    description: "Enable getDeviceId API in OpEventProxyInfo"
+    bug: "337340961"
+}
\ No newline at end of file
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 8dee4b1..c674968 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -70,6 +70,11 @@
  * For text that will not change, use a {@link StaticLayout}.
  */
 public abstract class Layout {
+
+    // These should match the constants in framework/base/libs/hwui/hwui/DrawTextFunctor.h
+    private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX = 4f;
+    private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR = 0.2f;
+
     /** @hide */
     @IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
             LineBreaker.BREAK_STRATEGY_SIMPLE,
@@ -494,9 +499,9 @@
 
         drawText(canvas, firstLine, lastLine);
 
-        // Since high contrast text draws a solid rectangle background behind the text, it covers up
-        // the highlights and selections. In this case we draw over the top of the text with a
-        // blend mode that ensures the text stays high-contrast.
+        // Since high contrast text draws a thick border on the text, the highlight actually makes
+        // it harder to read. In this case we draw over the top of the text with a blend mode that
+        // ensures the text stays high-contrast.
         if (shouldDrawHighlightsOnTop(canvas)) {
             drawHighlights(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
                     cursorOffsetVertical, firstLine, lastLine);
@@ -922,6 +927,9 @@
     public void drawBackground(
             @NonNull Canvas canvas,
             int firstLine, int lastLine) {
+
+        drawHighContrastBackground(canvas, firstLine, lastLine);
+
         // First, draw LineBackgroundSpans.
         // LineBackgroundSpans know nothing about the alignment, margins, or
         // direction of the layout or line.  XXX: Should they?
@@ -988,6 +996,66 @@
     }
 
     /**
+     * Draws a solid rectangle behind the text, the same color as the high contrast stroke border,
+     * to make it even easier to read.
+     *
+     * <p>We draw it here instead of in DrawTextFunctor so that multiple spans don't draw
+     * backgrounds over each other's text.
+     */
+    private void drawHighContrastBackground(@NonNull Canvas canvas, int firstLine, int lastLine) {
+        if (!shouldDrawHighlightsOnTop(canvas)) {
+            return;
+        }
+
+        var padding = Math.max(HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX,
+                mPaint.getTextSize() * HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR);
+
+        var bgPaint = mWorkPlainPaint;
+        bgPaint.reset();
+        bgPaint.setColor(isHighContrastTextDark() ? Color.WHITE : Color.BLACK);
+        bgPaint.setStyle(Paint.Style.FILL);
+
+        int start = getLineStart(firstLine);
+        int end = getLineEnd(lastLine);
+        // Draw a separate background rectangle for each line of text, that only surrounds the
+        // characters on that line.
+        forEachCharacterBounds(
+                start,
+                end,
+                firstLine,
+                lastLine,
+                new CharacterBoundsListener() {
+                    int mLastLineNum = -1;
+                    final RectF mLineBackground = new RectF();
+
+                    @Override
+                    public void onCharacterBounds(int index, int lineNum, float left, float top,
+                            float right, float bottom) {
+                        if (lineNum != mLastLineNum) {
+                            drawRect();
+                            mLineBackground.set(left, top, right, bottom);
+                            mLastLineNum = lineNum;
+                        } else {
+                            mLineBackground.union(left, top, right, bottom);
+                        }
+                    }
+
+                    @Override
+                    public void onEnd() {
+                        drawRect();
+                    }
+
+                    private void drawRect() {
+                        if (!mLineBackground.isEmpty()) {
+                            mLineBackground.inset(-padding, -padding);
+                            canvas.drawRect(mLineBackground, bgPaint);
+                        }
+                    }
+                }
+        );
+    }
+
+    /**
      * @param canvas
      * @return The range of lines that need to be drawn, possibly empty.
      * @hide
@@ -1682,7 +1750,7 @@
         }
 
         if (bounds == null) {
-            throw  new IllegalArgumentException("bounds can't be null.");
+            throw new IllegalArgumentException("bounds can't be null.");
         }
 
         final int neededLength = 4 * (end - start);
@@ -1698,6 +1766,34 @@
 
         final int startLine = getLineForOffset(start);
         final int endLine = getLineForOffset(end - 1);
+
+        forEachCharacterBounds(start, end, startLine, endLine,
+                (index, lineNum, left, lineTop, right, lineBottom) -> {
+                    final int boundsIndex = boundsStart + 4 * (index - start);
+                    bounds[boundsIndex] = left;
+                    bounds[boundsIndex + 1] = lineTop;
+                    bounds[boundsIndex + 2] = right;
+                    bounds[boundsIndex + 3] = lineBottom;
+                });
+    }
+
+    /**
+     * Return the characters' bounds in the given range. The coordinates are in local text layout.
+     *
+     * @param start the start index to compute the character bounds, inclusive.
+     * @param end the end index to compute the character bounds, exclusive.
+     * @param startLine index of the line that contains {@code start}
+     * @param endLine index of the line that contains {@code end}
+     * @param listener called for each character with its bounds
+     *
+     */
+    private void forEachCharacterBounds(
+            @IntRange(from = 0) int start,
+            @IntRange(from = 0) int end,
+            @IntRange(from = 0) int startLine,
+            @IntRange(from = 0) int endLine,
+            CharacterBoundsListener listener
+    ) {
         float[] horizontalBounds = null;
         for (int line = startLine; line <= endLine; ++line) {
             final int lineStart = getLineStart(line);
@@ -1722,13 +1818,10 @@
                 final float left = horizontalBounds[offset * 2] + lineStartPos;
                 final float right = horizontalBounds[offset * 2 + 1] + lineStartPos;
 
-                final int boundsIndex = boundsStart + 4 * (index - start);
-                bounds[boundsIndex] = left;
-                bounds[boundsIndex + 1] = lineTop;
-                bounds[boundsIndex + 2] = right;
-                bounds[boundsIndex + 3] = lineBottom;
+                listener.onCharacterBounds(index, line, left, lineTop, right, lineBottom);
             }
         }
+        listener.onEnd();
     }
 
     /**
@@ -4443,4 +4536,15 @@
     public Paint.FontMetrics getMinimumFontMetrics() {
         return mMinimumFontMetrics;
     }
+
+    /**
+     * Callback for {@link #forEachCharacterBounds(int, int, int, int, CharacterBoundsListener)}
+     */
+    private interface CharacterBoundsListener {
+        void onCharacterBounds(int index, int lineNum, float left, float top, float right,
+                float bottom);
+
+        /** Called after the last character has been sent to {@link #onCharacterBounds}. */
+        default void onEnd() {}
+    }
 }
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index b9de93c..7653bdb 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -56,11 +56,18 @@
     public static class BytesResult {
         public final String value;
         public final String units;
+        /**
+         * Content description of the {@link #units}.
+         * See {@link View#setContentDescription(CharSequence)}
+         */
+        public final String unitsContentDescription;
         public final long roundedBytes;
 
-        public BytesResult(String value, String units, long roundedBytes) {
+        public BytesResult(String value, String units, String unitsContentDescription,
+                long roundedBytes) {
             this.value = value;
             this.units = units;
+            this.unitsContentDescription = unitsContentDescription;
             this.roundedBytes = roundedBytes;
         }
     }
@@ -271,20 +278,20 @@
         final Locale locale = res.getConfiguration().getLocales().get(0);
         final NumberFormat numberFormatter = getNumberFormatter(locale, rounded.fractionDigits);
         final String formattedNumber = numberFormatter.format(rounded.value);
-        final String units;
+        // Since ICU does not give us access to the pattern, we need to extract the unit string
+        // from ICU, which we do by taking out the formatted number out of the formatted string
+        // and trimming the result of spaces and controls.
+        final String formattedMeasure = formatMeasureShort(
+                locale, numberFormatter, rounded.value, rounded.units);
+        final String numberRemoved = deleteFirstFromString(formattedMeasure, formattedNumber);
+        String units = SPACES_AND_CONTROLS.trim(numberRemoved).toString();
+        String unitsContentDescription = units;
         if (rounded.units == MeasureUnit.BYTE) {
             // ICU spells out "byte" instead of "B".
             units = getByteSuffixOverride(res);
-        } else {
-            // Since ICU does not give us access to the pattern, we need to extract the unit string
-            // from ICU, which we do by taking out the formatted number out of the formatted string
-            // and trimming the result of spaces and controls.
-            final String formattedMeasure = formatMeasureShort(
-                    locale, numberFormatter, rounded.value, rounded.units);
-            final String numberRemoved = deleteFirstFromString(formattedMeasure, formattedNumber);
-            units = SPACES_AND_CONTROLS.trim(numberRemoved).toString();
         }
-        return new BytesResult(formattedNumber, units, rounded.roundedBytes);
+        return new BytesResult(formattedNumber, units, unitsContentDescription,
+                rounded.roundedBytes);
     }
 
     /**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ddead88..a844370 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -912,12 +912,6 @@
     private static final String AUTOFILL_LOG_TAG = "View.Autofill";
 
     /**
-     * The logging tag used by this class when logging verbose and chatty (high volume)
-     * autofill-related messages.
-     */
-    private static final String AUTOFILL_CHATTY_LOG_TAG = "View.Autofill.Chatty";
-
-    /**
      * The logging tag used by this class when logging content capture-related messages.
      */
     private static final String CONTENT_CAPTURE_LOG_TAG = "View.ContentCapture";
@@ -8708,8 +8702,8 @@
     @CallSuper
     protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction,
             @Nullable Rect previouslyFocusedRect) {
-        if (Log.isLoggable(AUTOFILL_CHATTY_LOG_TAG, Log.VERBOSE)) {
-            Log.v(AUTOFILL_CHATTY_LOG_TAG, "onFocusChanged() entered. gainFocus: "
+        if (DBG) {
+            Log.d(VIEW_LOG_TAG, "onFocusChanged() entered. gainFocus: "
                     + gainFocus);
         }
         if (gainFocus) {
@@ -8777,8 +8771,8 @@
         if (canNotifyAutofillEnterExitEvent()) {
             AutofillManager afm = getAutofillManager();
             if (afm != null) {
-                if (Log.isLoggable(AUTOFILL_CHATTY_LOG_TAG, Log.VERBOSE)) {
-                    Log.v(AUTOFILL_CHATTY_LOG_TAG, this + " afm is not null");
+                if (DBG) {
+                    Log.d(VIEW_LOG_TAG, this + " afm is not null");
                 }
                 if (enter) {
                     // We have not been laid out yet, hence cannot evaluate
@@ -8791,8 +8785,8 @@
                     // animation beginning. On the time, the view is not visible
                     // to the user. And then as the animation progresses, the view
                     // becomes visible to the user.
-                    if (Log.isLoggable(AUTOFILL_CHATTY_LOG_TAG, Log.VERBOSE)) {
-                        Log.v(AUTOFILL_CHATTY_LOG_TAG,
+                    if (DBG) {
+                        Log.d(VIEW_LOG_TAG,
                                 "notifyEnterOrExitForAutoFillIfNeeded:"
                                 + " isLaidOut(): " + isLaidOut()
                                 + " isVisibleToUser(): " + isVisibleToUser()
@@ -11024,28 +11018,28 @@
     }
 
     private boolean isAutofillable() {
-        if (Log.isLoggable(AUTOFILL_CHATTY_LOG_TAG, Log.VERBOSE)) {
-            Log.v(AUTOFILL_CHATTY_LOG_TAG, "isAutofillable() entered.");
+        if (DBG) {
+            Log.d(VIEW_LOG_TAG, "isAutofillable() entered.");
         }
         if (getAutofillType() == AUTOFILL_TYPE_NONE) {
-            if (Log.isLoggable(AUTOFILL_CHATTY_LOG_TAG, Log.VERBOSE)) {
-                Log.v(AUTOFILL_CHATTY_LOG_TAG, "getAutofillType() returns AUTOFILL_TYPE_NONE");
+            if (DBG) {
+                Log.d(VIEW_LOG_TAG, "getAutofillType() returns AUTOFILL_TYPE_NONE");
             }
             return false;
         }
 
         final AutofillManager afm = getAutofillManager();
         if (afm == null) {
-            if (Log.isLoggable(AUTOFILL_CHATTY_LOG_TAG, Log.VERBOSE)) {
-                Log.v(AUTOFILL_CHATTY_LOG_TAG, "AutofillManager is null");
+            if (DBG) {
+                Log.d(VIEW_LOG_TAG, "AutofillManager is null");
             }
             return false;
         }
 
         // Check whether view is not part of an activity. If it's not, return false.
         if (getAutofillViewId() <= LAST_APP_AUTOFILL_ID) {
-            if (Log.isLoggable(AUTOFILL_CHATTY_LOG_TAG, Log.VERBOSE)) {
-                Log.v(AUTOFILL_CHATTY_LOG_TAG, "getAutofillViewId()<=LAST_APP_AUTOFILL_ID");
+            if (DBG) {
+                Log.d(VIEW_LOG_TAG, "getAutofillViewId()<=LAST_APP_AUTOFILL_ID");
             }
             return false;
         }
@@ -11056,9 +11050,8 @@
         if ((isImportantForAutofill() && afm.isTriggerFillRequestOnFilteredImportantViewsEnabled())
                 || (!isImportantForAutofill()
                     && afm.isTriggerFillRequestOnUnimportantViewEnabled())) {
-            if (Log.isLoggable(AUTOFILL_CHATTY_LOG_TAG, Log.VERBOSE)) {
-                Log.v(AUTOFILL_CHATTY_LOG_TAG,
-                        "isImportantForAutofill(): " + isImportantForAutofill()
+            if (DBG) {
+                Log.d(VIEW_LOG_TAG, "isImportantForAutofill(): " + isImportantForAutofill()
                         + "afm.isAutofillable(): " + afm.isAutofillable(this));
             }
             return afm.isAutofillable(this) ? true : notifyAugmentedAutofillIfNeeded(afm);
@@ -11066,8 +11059,8 @@
 
         // If the previous condition is not met, fall back to the previous way to trigger fill
         // request based on autofill importance instead.
-        if (Log.isLoggable(AUTOFILL_CHATTY_LOG_TAG, Log.VERBOSE)) {
-            Log.v(AUTOFILL_CHATTY_LOG_TAG, "isImportantForAutofill(): " + isImportantForAutofill());
+        if (DBG) {
+            Log.d(VIEW_LOG_TAG, "isImportantForAutofill(): " + isImportantForAutofill());
         }
         return isImportantForAutofill() ? true : notifyAugmentedAutofillIfNeeded(afm);
     }
@@ -11083,8 +11076,8 @@
 
     /** @hide */
     public boolean canNotifyAutofillEnterExitEvent() {
-        if (Log.isLoggable(AUTOFILL_CHATTY_LOG_TAG, Log.VERBOSE)) {
-            Log.v(AUTOFILL_CHATTY_LOG_TAG, "canNotifyAutofillEnterExitEvent() entered. "
+        if (DBG) {
+            Log.d(VIEW_LOG_TAG, "canNotifyAutofillEnterExitEvent() entered. "
                     + " isAutofillable(): " + isAutofillable()
                     + " isAttachedToWindow(): " + isAttachedToWindow());
         }
@@ -17160,10 +17153,12 @@
 
     /**
      * Handle a key event before it is processed by any input method
-     * associated with the view hierarchy.  This can be used to intercept
+     * associated with the view hierarchy. This can be used to intercept
      * key events in special situations before the IME consumes them; a
      * typical example would be handling the BACK key to update the application's
-     * UI instead of allowing the IME to see it and close itself.
+     * UI instead of allowing the IME to see it and close itself. Due to a bug,
+     * this function is not called for BACK key events on Android T and U, when
+     * the IME is shown.
      *
      * @param keyCode The value in event.getKeyCode().
      * @param event Description of the key event.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f0d27da..f51d909 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4257,8 +4257,8 @@
         // when the values are applicable.
         if (mDrawnThisFrame) {
             mDrawnThisFrame = false;
-            updateInfrequentCount();
             setCategoryFromCategoryCounts();
+            updateInfrequentCount();
             setPreferredFrameRate(mPreferredFrameRate);
             setPreferredFrameRateCategory(mPreferredFrameRateCategory);
             if (!mIsFrameRateConflicted) {
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index ee6ba24..3b9b162 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -16,6 +16,8 @@
 
 package android.window;
 
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Bundle;
@@ -27,6 +29,8 @@
 import android.util.Log;
 import android.view.ViewRootImpl;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 
 /**
@@ -280,7 +284,8 @@
         sendStopDispatching();
     }
 
-    static class ImeOnBackInvokedCallback implements OnBackInvokedCallback {
+    @VisibleForTesting(visibility = PACKAGE)
+    public static class ImeOnBackInvokedCallback implements OnBackInvokedCallback {
         @NonNull
         private final IOnBackInvokedCallback mIOnBackInvokedCallback;
         /**
@@ -327,7 +332,8 @@
      * Subclass of ImeOnBackInvokedCallback indicating that a predictive IME back animation may be
      * played instead of invoking the callback.
      */
-    static class DefaultImeOnBackAnimationCallback extends ImeOnBackInvokedCallback {
+    @VisibleForTesting(visibility = PACKAGE)
+    public static class DefaultImeOnBackAnimationCallback extends ImeOnBackInvokedCallback {
         DefaultImeOnBackAnimationCallback(@NonNull IOnBackInvokedCallback iCallback, int id,
                 int priority) {
             super(iCallback, id, priority);
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 7f6678e..cf0c015 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -176,6 +176,12 @@
                 mImeDispatcher.registerOnBackInvokedCallback(priority, callback);
                 return;
             }
+            if ((callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback
+                    || callback instanceof ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback)
+                    && !isOnBackInvokedCallbackEnabled()) {
+                // Fall back to compat back key injection if legacy back behaviour should be used.
+                return;
+            }
             if (!mOnBackInvokedCallbacks.containsKey(priority)) {
                 mOnBackInvokedCallbacks.put(priority, new ArrayList<>());
             }
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 98ff3c6..cd13c4a 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -2,6 +2,13 @@
 container: "system"
 
 flag {
+  name: "disable_thin_letterboxing_reachability"
+  namespace: "large_screen_experiences_app_compat"
+  description: "Whether reachability is disabled in case of thin letterboxing"
+  bug: "334077350"
+}
+
+flag {
   name: "allows_screen_size_decoupled_from_status_bar_and_cutout"
   namespace: "large_screen_experiences_app_compat"
   description: "When necessary, configuration decoupled from status bar and display cutout"
diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp
index a022842..7267eb8 100644
--- a/core/jni/android_hardware_UsbDeviceConnection.cpp
+++ b/core/jni/android_hardware_UsbDeviceConnection.cpp
@@ -190,17 +190,21 @@
         return -1;
     }
 
-    jbyte* bufferBytes = NULL;
-    if (buffer) {
-        bufferBytes = (jbyte*)env->GetPrimitiveArrayCritical(buffer, NULL);
+    bool is_dir_in = (endpoint & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN;
+    jbyte *bufferBytes = (jbyte *)malloc(length);
+
+    if (!is_dir_in && buffer) {
+        env->GetByteArrayRegion(buffer, start, length, bufferBytes);
     }
 
-    jint result = usb_device_bulk_transfer(device, endpoint, bufferBytes + start, length, timeout);
+    jint result = usb_device_bulk_transfer(device, endpoint, bufferBytes, length, timeout);
 
-    if (bufferBytes) {
-        env->ReleasePrimitiveArrayCritical(buffer, bufferBytes, 0);
+    if (is_dir_in && buffer) {
+        env->SetByteArrayRegion(buffer, start, length, bufferBytes);
     }
 
+    free(bufferBytes);
+
     return result;
 }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e2106c5..77a9912 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8233,7 +8233,7 @@
         </activity>
         <activity android:name="com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity"
                   android:exported="false"
-                  android:theme="@style/Theme.DeviceDefault.Resolver"
+                  android:theme="@style/AccessibilityButtonChooserDialog"
                   android:finishOnCloseSystemDialogs="true"
                   android:excludeFromRecents="true"
                   android:documentLaunchMode="never"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0d1be38..877d11e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -764,6 +764,9 @@
     <!-- Indicates whether to enable hinge angle sensor when using unfold animation -->
     <bool name="config_unfoldTransitionHingeAngle">false</bool>
 
+    <!-- Indicates whether to enable haptics during unfold animation -->
+    <bool name="config_unfoldTransitionHapticsEnabled">false</bool>
+
     <!-- Indicates the time needed to time out the fold animation if the device stops in half folded
          mode. -->
     <integer name="config_unfoldTransitionHalfFoldedTimeout">1000</integer>
@@ -7035,4 +7038,7 @@
          If the gesture is completed faster than this, we assume it's not performed by human and the
          event gets ignored. -->
     <integer name="config_defaultMinEmergencyGestureTapDurationMillis">200</integer>
+
+    <!-- Whether the system uses auto-suspend mode. -->
+    <bool name="config_useAutoSuspend">true</bool>
 </resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 5945f81..a46dc04 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1685,4 +1685,9 @@
         <item name="android:paddingStart">8dp</item>
         <item name="android:background">?android:attr/selectableItemBackground</item>
     </style>
+
+    <style name="AccessibilityButtonChooserDialog"
+           parent="@style/Theme.DeviceDefault.Resolver">
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+    </style>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index daf1230..972d8df 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4219,6 +4219,7 @@
   <java-symbol type="bool" name="config_supportsConcurrentInternalDisplays" />
   <java-symbol type="bool" name="config_unfoldTransitionEnabled" />
   <java-symbol type="bool" name="config_unfoldTransitionHingeAngle" />
+  <java-symbol type="bool" name="config_unfoldTransitionHapticsEnabled" />
   <java-symbol type="integer" name="config_unfoldTransitionHalfFoldedTimeout" />
   <java-symbol type="array" name="config_perDeviceStateRotationLockDefaults" />
 
@@ -5423,4 +5424,7 @@
   <!-- Back swipe thresholds -->
   <java-symbol type="dimen" name="navigation_edge_action_progress_threshold" />
   <java-symbol type="dimen" name="back_progress_non_linear_factor" />
+
+  <!-- For PowerManagerService to determine whether to use auto-suspend mode -->
+  <java-symbol type="bool" name="config_useAutoSuspend" />
 </resources>
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 5cc1ee4..7fb894a 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -86,6 +86,7 @@
 import android.os.Parcelable;
 import android.os.SystemProperties;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
@@ -126,6 +127,7 @@
 
     @Rule
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     @Before
     public void setUp() {
@@ -1835,6 +1837,36 @@
         Assert.assertEquals(bitmap, resultBitmap);
     }
 
+    @Test
+    public void testGetWhen_zero() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setWhen(0)
+                .build();
+
+        mSetFlagsRule.disableFlags(Flags.FLAG_SORT_SECTION_BY_TIME);
+
+        assertThat(n.getWhen()).isEqualTo(0);
+
+        mSetFlagsRule.enableFlags(Flags.FLAG_SORT_SECTION_BY_TIME);
+
+        assertThat(n.getWhen()).isEqualTo(n.creationTime);
+    }
+
+    @Test
+    public void testGetWhen_devProvidedNonZero() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setWhen(9)
+                .build();
+
+        mSetFlagsRule.disableFlags(Flags.FLAG_SORT_SECTION_BY_TIME);
+
+        assertThat(n.getWhen()).isEqualTo(9);
+
+        mSetFlagsRule.enableFlags(Flags.FLAG_SORT_SECTION_BY_TIME);
+
+        assertThat(n.getWhen()).isEqualTo(9);
+    }
+
     private void assertValid(Notification.Colors c) {
         // Assert that all colors are populated
         assertThat(c.getBackgroundColor()).isNotEqualTo(Notification.COLOR_INVALID);
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index f60eff6..1c12362 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -18,9 +18,6 @@
 
 import static com.android.graphics.hwui.flags.Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT;
 
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -47,6 +44,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.google.common.truth.Expect;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -63,7 +62,13 @@
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
+    @Rule
+    public final Expect expect = Expect.create();
+
+    // Line count when using MockLayout
     private static final int LINE_COUNT = 5;
+    // Actual line count when using StaticLayout
+    private static final int STATIC_LINE_COUNT = 9;
     private static final int LINE_HEIGHT = 12;
     private static final int LINE_DESCENT = 4;
     private static final CharSequence LAYOUT_TEXT = "alwei\t;sdfs\ndf @";
@@ -655,8 +660,8 @@
     @Test
     @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
     public void highContrastTextEnabled_testDrawSelectionAndHighlight_drawsHighContrastSelectionAndHighlight() {
-        Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
-                mAlign, mSpacingMult, mSpacingAdd);
+        Layout layout = new StaticLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+                mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
 
         List<Path> highlightPaths = new ArrayList<>();
         List<Paint> highlightPaints = new ArrayList<>();
@@ -677,9 +682,12 @@
         layout.draw(c, highlightPaths, highlightPaints, selectionPath, selectionPaint,
                 /* cursorOffsetVertical= */ 0);
         List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
-        var textsDrawn = LINE_COUNT;
+        var textsDrawn = STATIC_LINE_COUNT;
         var highlightsDrawn = 2;
-        assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+        var backgroundRectsDrawn = STATIC_LINE_COUNT;
+        expect.withMessage("wrong number of drawCommands: " + drawCommands)
+                .that(drawCommands.size())
+                .isEqualTo(textsDrawn + backgroundRectsDrawn + highlightsDrawn);
 
         var highlightsFound = 0;
         var curLineIndex = 0;
@@ -687,29 +695,26 @@
             MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
 
             if (drawCommand.path != null) {
-                assertThat(drawCommand.path).isEqualTo(selectionPath);
-                assertThat(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
-                assertThat(drawCommand.paint.getBlendMode()).isNotNull();
+                expect.that(drawCommand.path).isEqualTo(selectionPath);
+                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+                expect.that(drawCommand.paint.getBlendMode()).isNotNull();
                 highlightsFound++;
             } else if (drawCommand.text != null) {
-                int start = layout.getLineStart(curLineIndex);
-                int end = layout.getLineEnd(curLineIndex);
-                assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
                 curLineIndex++;
 
-                assertWithMessage("highlight is drawn on top of text")
+                expect.withMessage("highlight is drawn on top of text")
                         .that(highlightsFound).isEqualTo(0);
             }
         }
 
-        assertThat(highlightsFound).isEqualTo(2);
+        expect.that(highlightsFound).isEqualTo(2);
     }
 
     @Test
     @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
     public void highContrastTextEnabled_testDrawHighlight_drawsHighContrastHighlight() {
-        Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
-                mAlign, mSpacingMult, mSpacingAdd);
+        Layout layout = new StaticLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+                mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
 
         List<Path> highlightPaths = new ArrayList<>();
         List<Paint> highlightPaints = new ArrayList<>();
@@ -730,9 +735,12 @@
         layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
                 /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
         List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
-        var textsDrawn = LINE_COUNT;
+        var textsDrawn = STATIC_LINE_COUNT;
         var highlightsDrawn = 1;
-        assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+        var backgroundRectsDrawn = STATIC_LINE_COUNT;
+        expect.withMessage("wrong number of drawCommands: " + drawCommands)
+                .that(drawCommands.size())
+                .isEqualTo(textsDrawn + backgroundRectsDrawn + highlightsDrawn);
 
         var highlightsFound = 0;
         var curLineIndex = 0;
@@ -740,29 +748,26 @@
             MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
 
             if (drawCommand.path != null) {
-                assertThat(drawCommand.path).isEqualTo(selectionPath);
-                assertThat(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
-                assertThat(drawCommand.paint.getBlendMode()).isNotNull();
+                expect.that(drawCommand.path).isEqualTo(selectionPath);
+                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+                expect.that(drawCommand.paint.getBlendMode()).isNotNull();
                 highlightsFound++;
             } else if (drawCommand.text != null) {
-                int start = layout.getLineStart(curLineIndex);
-                int end = layout.getLineEnd(curLineIndex);
-                assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
                 curLineIndex++;
 
-                assertWithMessage("highlight is drawn on top of text")
+                expect.withMessage("highlight is drawn on top of text")
                         .that(highlightsFound).isEqualTo(0);
             }
         }
 
-        assertThat(highlightsFound).isEqualTo(1);
+        expect.that(highlightsFound).isEqualTo(1);
     }
 
     @Test
     @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
     public void highContrastTextDisabledByDefault_testDrawHighlight_drawsNormalHighlightBehind() {
-        Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
-                mAlign, mSpacingMult, mSpacingAdd);
+        Layout layout = new StaticLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+                mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
 
         List<Path> highlightPaths = new ArrayList<>();
         List<Paint> highlightPaints = new ArrayList<>();
@@ -782,9 +787,12 @@
         layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
                 /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
         List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
-        var textsDrawn = LINE_COUNT;
+        var textsDrawn = STATIC_LINE_COUNT;
         var highlightsDrawn = 1;
-        assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+        var backgroundRectsDrawn = 0;
+        expect.withMessage("wrong number of drawCommands: " + drawCommands)
+                .that(drawCommands.size())
+                .isEqualTo(textsDrawn + backgroundRectsDrawn + highlightsDrawn);
 
         var highlightsFound = 0;
         var curLineIndex = 0;
@@ -792,29 +800,26 @@
             MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
 
             if (drawCommand.path != null) {
-                assertThat(drawCommand.path).isEqualTo(selectionPath);
-                assertThat(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
-                assertThat(drawCommand.paint.getBlendMode()).isNull();
+                expect.that(drawCommand.path).isEqualTo(selectionPath);
+                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+                expect.that(drawCommand.paint.getBlendMode()).isNull();
                 highlightsFound++;
             } else if (drawCommand.text != null) {
-                int start = layout.getLineStart(curLineIndex);
-                int end = layout.getLineEnd(curLineIndex);
-                assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
                 curLineIndex++;
 
-                assertWithMessage("highlight is drawn behind text")
+                expect.withMessage("highlight is drawn behind text")
                         .that(highlightsFound).isGreaterThan(0);
             }
         }
 
-        assertThat(highlightsFound).isEqualTo(1);
+        expect.that(highlightsFound).isEqualTo(1);
     }
 
     @Test
     @RequiresFlagsDisabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
     public void highContrastTextEnabledButFlagOff_testDrawHighlight_drawsNormalHighlightBehind() {
-        Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
-                mAlign, mSpacingMult, mSpacingAdd);
+        Layout layout = new StaticLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+                mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
 
         List<Path> highlightPaths = new ArrayList<>();
         List<Paint> highlightPaints = new ArrayList<>();
@@ -835,9 +840,12 @@
         layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
                 /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
         List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
-        var textsDrawn = LINE_COUNT;
+        var textsDrawn = STATIC_LINE_COUNT;
         var highlightsDrawn = 1;
-        assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+        var backgroundRectsDrawn = 0;
+        expect.withMessage("wrong number of drawCommands: " + drawCommands)
+                .that(drawCommands.size())
+                .isEqualTo(textsDrawn + backgroundRectsDrawn + highlightsDrawn);
 
         var highlightsFound = 0;
         var curLineIndex = 0;
@@ -845,33 +853,84 @@
             MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
 
             if (drawCommand.path != null) {
-                assertThat(drawCommand.path).isEqualTo(selectionPath);
-                assertThat(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
-                assertThat(drawCommand.paint.getBlendMode()).isNull();
+                expect.that(drawCommand.path).isEqualTo(selectionPath);
+                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+                expect.that(drawCommand.paint.getBlendMode()).isNull();
                 highlightsFound++;
             } else if (drawCommand.text != null) {
-                int start = layout.getLineStart(curLineIndex);
-                int end = layout.getLineEnd(curLineIndex);
-                assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
                 curLineIndex++;
 
-                assertWithMessage("highlight is drawn behind text")
+                expect.withMessage("highlight is drawn behind text")
                         .that(highlightsFound).isGreaterThan(0);
             }
         }
 
-        assertThat(highlightsFound).isEqualTo(1);
+        expect.that(highlightsFound).isEqualTo(1);
     }
 
     @Test
     public void mockCanvasHighContrastOverridesCorrectly() {
         var canvas = new MockCanvas(100, 100);
 
-        assertThat(canvas.isHighContrastTextEnabled()).isFalse();
+        expect.that(canvas.isHighContrastTextEnabled()).isFalse();
         canvas.setHighContrastTextEnabled(true);
-        assertThat(canvas.isHighContrastTextEnabled()).isTrue();
+        expect.that(canvas.isHighContrastTextEnabled()).isTrue();
         canvas.setHighContrastTextEnabled(false);
-        assertThat(canvas.isHighContrastTextEnabled()).isFalse();
+        expect.that(canvas.isHighContrastTextEnabled()).isFalse();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+    public void highContrastTextEnabled_testDrawLightText_drawsBlackBackgroundRects() {
+        mTextPaint.setColor(Color.parseColor("#CCAA33"));
+        Layout layout = new StaticLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+                mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
+
+        final int width = 256;
+        final int height = 256;
+        MockCanvas c = new MockCanvas(width, height);
+        c.setHighContrastTextEnabled(true);
+        layout.draw(
+                c,
+                /* highlightPaths= */ null,
+                /* highlightPaints= */ null,
+                /* selectionPath= */ null,
+                /* selectionPaint= */ null,
+                /* cursorOffsetVertical= */ 0
+        );
+        List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+        var textsDrawn = STATIC_LINE_COUNT;
+        var highlightsDrawn = 0;
+        var backgroundRectsDrawn = STATIC_LINE_COUNT;
+        expect.withMessage("wrong number of drawCommands: " + drawCommands)
+                .that(drawCommands.size())
+                .isEqualTo(textsDrawn + backgroundRectsDrawn + highlightsDrawn);
+
+        int numBackgroundsFound = 0;
+        var curLineIndex = 0;
+        for (int i = 0; i < drawCommands.size(); i++) {
+            MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+            if (drawCommand.rect != null) {
+                numBackgroundsFound++;
+                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.BLACK);
+                expect.that(drawCommand.rect.height()).isAtLeast(LINE_HEIGHT);
+                expect.that(drawCommand.rect.width()).isGreaterThan(0);
+                float expectedY = (numBackgroundsFound) * (LINE_HEIGHT + LINE_DESCENT);
+                expect.that(drawCommand.rect.bottom).isAtLeast(expectedY);
+            } else if (drawCommand.text != null) {
+                // draw text
+                curLineIndex++;
+
+                expect.withMessage("background is drawn on top of text")
+                        .that(numBackgroundsFound).isEqualTo(backgroundRectsDrawn);
+            } else {
+                fail("unexpected path drawn");
+            }
+        }
+
+        // One for each line
+        expect.that(numBackgroundsFound).isEqualTo(backgroundRectsDrawn);
     }
 
     private static final class MockCanvas extends Canvas {
@@ -881,22 +940,46 @@
             public final float x;
             public final float y;
             public final Path path;
+            public final RectF rect;
             public final Paint paint;
 
             DrawCommand(String text, float x, float y, Paint paint) {
                 this.text = text;
                 this.x = x;
                 this.y = y;
-                this.paint = paint;
+                this.paint = new Paint(paint);
                 path = null;
+                rect = null;
             }
 
             DrawCommand(Path path, Paint paint) {
                 this.path = path;
-                this.paint = paint;
+                this.paint = new Paint(paint);
                 y = 0;
                 x = 0;
                 text = null;
+                rect = null;
+            }
+
+            DrawCommand(RectF rect, Paint paint) {
+                this.rect = new RectF(rect);
+                this.paint = new Paint(paint);
+                path = null;
+                y = 0;
+                x = 0;
+                text = null;
+            }
+
+            @Override
+            public String toString() {
+                return "DrawCommand{"
+                        + "text='" + text + '\''
+                        + ", x=" + x
+                        + ", y=" + y
+                        + ", path=" + path
+                        + ", rect=" + rect
+                        + ", paint=" + paint
+                        + '}';
             }
         }
 
@@ -956,6 +1039,11 @@
             mDrawCommands.add(new DrawCommand(path, p));
         }
 
+        @Override
+        public void drawRect(RectF rect, Paint p) {
+            mDrawCommands.add(new DrawCommand(rect, p));
+        }
+
         List<DrawCommand> getDrawCommands() {
             return mDrawCommands;
         }
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 5caf77db..7c098f2 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -1138,6 +1138,8 @@
         mView = new View(sContext);
         WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
         wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
+        int expected = toolkitFrameRateDefaultNormalReadOnly()
+                    ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
 
         sInstrumentation.runOnMainSync(() -> {
             WindowManager wm = sContext.getSystemService(WindowManager.class);
@@ -1157,8 +1159,6 @@
         Thread.sleep(delay);
         sInstrumentation.runOnMainSync(() -> {
             mView.invalidate();
-            int expected = toolkitFrameRateDefaultNormalReadOnly()
-                    ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
             runAfterDraw(() -> assertEquals(expected,
                     mViewRootImpl.getLastPreferredFrameRateCategory()));
         });
@@ -1168,8 +1168,6 @@
         Thread.sleep(delay);
         sInstrumentation.runOnMainSync(() -> {
             mView.invalidate();
-            int expected = toolkitFrameRateDefaultNormalReadOnly()
-                    ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
             runAfterDraw(() -> assertEquals(expected,
                     mViewRootImpl.getLastPreferredFrameRateCategory()));
         });
@@ -1177,12 +1175,26 @@
         // Infrequent update
         Thread.sleep(delay);
         sInstrumentation.runOnMainSync(() -> {
-            mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
             mView.invalidate();
             runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NORMAL,
                     mViewRootImpl.getLastPreferredFrameRateCategory()));
         });
         waitForAfterDraw();
+
+        // When the View vote, it's still considered as intermittent update state
+        sInstrumentation.runOnMainSync(() -> {
+            mView.invalidate();
+            runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NORMAL,
+                    mViewRootImpl.getLastPreferredFrameRateCategory()));
+        });
+        waitForAfterDraw();
+
+        // Becomes frequent update state
+        sInstrumentation.runOnMainSync(() -> {
+            mView.invalidate();
+            runAfterDraw(() -> assertEquals(expected,
+                    mViewRootImpl.getLastPreferredFrameRateCategory()));
+        });
     }
 
     /**
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 852d696..1a8e75a 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -40,6 +40,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.view.IWindow;
 import android.view.IWindowSession;
+import android.view.ImeBackAnimationController;
 import android.view.MotionEvent;
 
 import androidx.test.filters.SmallTest;
@@ -77,6 +78,12 @@
     @Mock
     private OnBackAnimationCallback mCallback2;
     @Mock
+    private ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback mImeCallback;
+    @Mock
+    private ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback mDefaultImeCallback;
+    @Mock
+    private ImeBackAnimationController mImeBackAnimationController;
+    @Mock
     private Context mContext;
     @Mock
     private ApplicationInfo mApplicationInfo;
@@ -103,7 +110,7 @@
         doReturn(mApplicationInfo).when(mContext).getApplicationInfo();
 
         mDispatcher = new WindowOnBackInvokedDispatcher(mContext, Looper.getMainLooper());
-        mDispatcher.attachToWindow(mWindowSession, mWindow, null);
+        mDispatcher.attachToWindow(mWindowSession, mWindow, mImeBackAnimationController);
     }
 
     private void waitForIdle() {
@@ -419,4 +426,28 @@
         // onBackPressed is called from animator, so it can happen more than once.
         verify(mCallback1, atLeast(1)).onBackProgressed(any());
     }
+
+    @Test
+    public void registerImeCallbacks_onBackInvokedCallbackEnabled() throws RemoteException {
+        mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mDefaultImeCallback);
+        assertCallbacksSize(/* default */ 1, /* overlay */ 0);
+        assertSetCallbackInfo();
+        assertTopCallback(mImeBackAnimationController);
+
+        mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mImeCallback);
+        assertCallbacksSize(/* default */ 2, /* overlay */ 0);
+        assertSetCallbackInfo();
+        assertTopCallback(mImeCallback);
+    }
+
+    @Test
+    public void registerImeCallbacks_legacyBack() throws RemoteException {
+        doReturn(false).when(mApplicationInfo).isOnBackInvokedCallbackEnabled();
+
+        mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mDefaultImeCallback);
+        assertNoSetCallbackInfo();
+
+        mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mImeCallback);
+        assertNoSetCallbackInfo();
+    }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 89d3058..e38038e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -1520,12 +1520,16 @@
     @GuardedBy("mLock")
     TaskFragmentContainer resolveStartActivityIntent(@NonNull WindowContainerTransaction wct,
             int taskId, @NonNull Intent intent, @Nullable Activity launchingActivity) {
-        // Skip resolving if started from an isolated navigated TaskFragmentContainer.
         if (launchingActivity != null) {
             final TaskFragmentContainer taskFragmentContainer = getContainerWithActivity(
                     launchingActivity);
             if (taskFragmentContainer != null
                     && taskFragmentContainer.isIsolatedNavigationEnabled()) {
+                // Skip resolving if started from an isolated navigated TaskFragmentContainer.
+                return null;
+            }
+            if (isAssociatedWithOverlay(launchingActivity)) {
+                // Skip resolving if the launching activity associated with an overlay.
                 return null;
             }
         }
@@ -1659,9 +1663,8 @@
         // is the first embedded TF in the task.
         final TaskContainer taskContainer = container.getTaskContainer();
         // TODO(b/265271880): remove redundant logic after all TF operations take fragmentToken.
-        final Rect taskBounds = taskContainer.getBounds();
         final Rect sanitizedBounds = sanitizeBounds(activityStackAttributes.getRelativeBounds(),
-                getMinDimensions(intent), taskBounds);
+                getMinDimensions(intent), container);
         final int windowingMode = taskContainer
                 .getWindowingModeForTaskFragment(sanitizedBounds);
         mPresenter.createTaskFragment(wct, taskFragmentToken, activityInTask.getActivityToken(),
@@ -1722,17 +1725,14 @@
     @GuardedBy("mLock")
     @Nullable
     TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken) {
-        // Check pending appeared activity first because there can be a delay for the server
-        // update.
-        TaskFragmentContainer taskFragmentContainer =
-                getContainer(container -> container.hasPendingAppearedActivity(activityToken));
-        if (taskFragmentContainer != null) {
-            return taskFragmentContainer;
+        for (int i = mTaskContainers.size() - 1; i >= 0; --i) {
+            final TaskFragmentContainer container = mTaskContainers.valueAt(i)
+                    .getContainerWithActivity(activityToken);
+            if (container != null) {
+                return container;
+            }
         }
-
-
-        // Check appeared activity if there is no such pending appeared activity.
-        return getContainer(container -> container.hasAppearedActivity(activityToken));
+        return null;
     }
 
     @GuardedBy("mLock")
@@ -2096,19 +2096,7 @@
         if (container == null) {
             return null;
         }
-        final List<SplitContainer> splitContainers =
-                container.getTaskContainer().getSplitContainers();
-        if (splitContainers.isEmpty()) {
-            return null;
-        }
-        for (int i = splitContainers.size() - 1; i >= 0; i--) {
-            final SplitContainer splitContainer = splitContainers.get(i);
-            if (container.equals(splitContainer.getSecondaryContainer())
-                    || container.equals(splitContainer.getPrimaryContainer())) {
-                return splitContainer;
-            }
-        }
-        return null;
+        return container.getTaskContainer().getActiveSplitForContainer(container);
     }
 
     /**
@@ -2158,6 +2146,11 @@
             return false;
         }
 
+        if (isAssociatedWithOverlay(activity)) {
+            // Can't launch the placeholder if the activity associates an overlay.
+            return false;
+        }
+
         final TaskFragmentContainer container = getContainerWithActivity(activity);
         if (container != null && !allowLaunchPlaceholder(container)) {
             // We don't allow activity in this TaskFragment to launch placeholder.
@@ -2197,6 +2190,11 @@
      */
     @GuardedBy("mLock")
     private boolean allowLaunchPlaceholder(@NonNull TaskFragmentContainer container) {
+        if (container.isOverlay()) {
+            // Don't launch placeholder if the container is an overlay.
+            return false;
+        }
+
         final TaskFragmentContainer topContainer = container.getTaskContainer()
                 .getTopNonFinishingTaskFragmentContainer();
         if (container != topContainer) {
@@ -2470,13 +2468,10 @@
     @GuardedBy("mLock")
     TaskFragmentContainer getContainer(@NonNull Predicate<TaskFragmentContainer> predicate) {
         for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
-            final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
-                    .getTaskFragmentContainers();
-            for (int j = containers.size() - 1; j >= 0; j--) {
-                final TaskFragmentContainer container = containers.get(j);
-                if (predicate.test(container)) {
-                    return container;
-                }
+            final TaskFragmentContainer container = mTaskContainers.valueAt(i)
+                    .getContainer(predicate);
+            if (container != null) {
+                return container;
             }
         }
         return null;
@@ -2641,6 +2636,16 @@
         return overlayContainers;
     }
 
+    @GuardedBy("mLock")
+    private boolean isAssociatedWithOverlay(@NonNull Activity activity) {
+        final TaskContainer taskContainer = getTaskContainer(getTaskId(activity));
+        if (taskContainer == null) {
+            return false;
+        }
+        return taskContainer.getContainer(c -> c.isOverlay() && !c.isFinished()
+                && c.getAssociatedActivityToken() == activity.getActivityToken()) != null;
+    }
+
     /**
      * Creates an overlay container or updates a visible overlay container if its
      * {@link TaskFragmentContainer#getTaskId()}, {@link TaskFragmentContainer#getOverlayTag()}
@@ -2734,7 +2739,7 @@
                 }
                 // Requesting an always-on-top overlay.
                 if (!associateLaunchingActivity) {
-                    if (overlayContainer.isAssociatedWithActivity()) {
+                    if (overlayContainer.isOverlayWithActivityAssociation()) {
                         // Dismiss the overlay container since it has associated with an activity.
                         Log.w(TAG, "The overlay container with tag:"
                                 + overlayContainer.getOverlayTag() + " is dismissed because"
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 6231ea0..0e4fb30 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -591,16 +591,14 @@
             @NonNull TaskFragmentContainer container,
             @NonNull ActivityStackAttributes attributes,
             @Nullable Size minDimensions) {
-        final Rect taskBounds = container.getTaskContainer().getBounds();
         final Rect relativeBounds = sanitizeBounds(attributes.getRelativeBounds(), minDimensions,
-                taskBounds);
+                container);
         final boolean isFillParent = relativeBounds.isEmpty();
         // Note that we only set isolated navigation for overlay container without activity
         // association. Activity will be launched to an expanded container on top of the overlay
         // if the overlay is associated with an activity. Thus, an overlay with activity association
         // will never be isolated navigated.
-        final boolean isIsolatedNavigated = container.isOverlay()
-                && !container.isAssociatedWithActivity() && !isFillParent;
+        final boolean isIsolatedNavigated = container.isAlwaysOnTopOverlay() && !isFillParent;
         final boolean dimOnTask = !isFillParent
                 && attributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK
                 && Flags.fullscreenDimFlag();
@@ -624,7 +622,7 @@
      */
     @NonNull
     static Rect sanitizeBounds(@NonNull Rect bounds, @Nullable Size minDimension,
-                               @NonNull Rect taskBounds) {
+                        @NonNull TaskFragmentContainer container) {
         if (bounds.isEmpty()) {
             // Don't need to check if the bounds follows the task bounds.
             return bounds;
@@ -633,10 +631,33 @@
             // Expand the bounds if the bounds are smaller than minimum dimensions.
             return new Rect();
         }
+        final TaskContainer taskContainer = container.getTaskContainer();
+        final Rect taskBounds = taskContainer.getBounds();
         if (!taskBounds.contains(bounds)) {
             // Expand the bounds if the bounds exceed the task bounds.
             return new Rect();
         }
+
+        if (!container.isOverlay()) {
+            // Stop here if the container is not an overlay.
+            return bounds;
+        }
+
+        final IBinder associatedActivityToken = container.getAssociatedActivityToken();
+
+        if (associatedActivityToken == null) {
+            // Stop here if the container is an always-on-top overlay.
+            return bounds;
+        }
+
+        // Expand the overlay with activity association if the associated activity is part of a
+        // split, or we may need to handle three change transition together.
+        final TaskFragmentContainer associatedContainer = taskContainer
+                .getContainerWithActivity(associatedActivityToken);
+        if (taskContainer.getActiveSplitForContainer(associatedContainer) != null) {
+            return new Rect();
+        }
+
         return bounds;
     }
 
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index fdf0910..67d34c7 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -43,6 +43,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.window.extensions.core.util.function.Predicate;
 import androidx.window.extensions.embedding.SplitAttributes.SplitType;
 import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType;
 import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType;
@@ -318,6 +319,38 @@
         return null;
     }
 
+    @Nullable
+    TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken) {
+        return getContainer(container -> container.hasAppearedActivity(activityToken)
+                || container.hasPendingAppearedActivity(activityToken));
+    }
+
+    @Nullable
+    TaskFragmentContainer getContainer(@NonNull Predicate<TaskFragmentContainer> predicate) {
+        for (int i = mContainers.size() - 1; i >= 0; i--) {
+            final TaskFragmentContainer container = mContainers.get(i);
+            if (predicate.test(container)) {
+                return container;
+            }
+        }
+        return null;
+    }
+
+    @Nullable
+    SplitContainer getActiveSplitForContainer(@Nullable TaskFragmentContainer container) {
+        if (container == null) {
+            return null;
+        }
+        for (int i = mSplitContainers.size() - 1; i >= 0; i--) {
+            final SplitContainer splitContainer = mSplitContainers.get(i);
+            if (container.equals(splitContainer.getSecondaryContainer())
+                    || container.equals(splitContainer.getPrimaryContainer())) {
+                return splitContainer;
+            }
+        }
+        return null;
+    }
+
     /**
      * Returns the always-on-top overlay container in the task, or {@code null} if it doesn't exist.
      */
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 094ebcb..c952dfe 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -465,7 +465,7 @@
             return;
         }
         // Early return if this container is not an overlay with activity association.
-        if (!isOverlay() || !isAssociatedWithActivity()) {
+        if (!isOverlayWithActivityAssociation()) {
             return;
         }
         if (mAssociatedActivityToken == activityToken) {
@@ -500,7 +500,7 @@
         // sure the controller considers this container as the one containing the activity.
         // This is needed when the activity is added as pending appeared activity to one
         // TaskFragment while it is also an appeared activity in another.
-        return mController.getContainerWithActivity(activityToken) == this;
+        return mTaskContainer.getContainerWithActivity(activityToken) == this;
     }
 
     /** Whether this activity has appeared in the TaskFragment on the server side. */
@@ -1019,16 +1019,16 @@
         return mAssociatedActivityToken;
     }
 
-    boolean isAssociatedWithActivity() {
-        return mAssociatedActivityToken != null;
-    }
-
     /**
      * Returns {@code true} if the overlay container should be always on top, which should be
      * a non-fill-parent overlay without activity association.
      */
     boolean isAlwaysOnTopOverlay() {
-        return isOverlay() && !isAssociatedWithActivity();
+        return isOverlay() && mAssociatedActivityToken == null;
+    }
+
+    boolean isOverlayWithActivityAssociation() {
+        return isOverlay() && mAssociatedActivityToken != null;
     }
 
     @Override
@@ -1050,7 +1050,7 @@
                 + " runningActivityCount=" + getRunningActivityCount()
                 + " isFinished=" + mIsFinished
                 + " overlayTag=" + mOverlayTag
-                + " associatedActivity" + mAssociatedActivityToken
+                + " associatedActivityToken=" + mAssociatedActivityToken
                 + " lastRequestedBounds=" + mLastRequestedBounds
                 + " pendingAppearedActivities=" + mPendingAppearedActivities
                 + (includeContainersToFinishOnExit ? " containersToFinishOnExit="
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 50abdfd..fab298d 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -21,11 +21,15 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_OVERLAY_TAG;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.SPLIT_ATTRIBUTES;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.TEST_TAG;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
+import static androidx.window.extensions.embedding.SplitPresenter.sanitizeBounds;
 import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_TASK;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -85,6 +89,7 @@
 import org.mockito.junit.MockitoRule;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -102,6 +107,9 @@
     @Rule
     public MockitoRule rule = MockitoJUnit.rule();
 
+    private static final Intent PLACEHOLDER_INTENT = new Intent().setComponent(
+            new ComponentName("test", "placeholder"));
+
     @Rule
     public final SetFlagsRule mSetFlagRule = new SetFlagsRule();
 
@@ -259,8 +267,7 @@
         mSplitController.setActivityStackAttributesCalculator(params ->
                 new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
         final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
-                mOverlayContainer1.getOverlayTag(),
-                mOverlayContainer1.getTopNonFinishingActivity());
+                mOverlayContainer1.getOverlayTag());
 
         assertWithMessage("overlayContainer1 must be updated since the new overlay container"
                 + " is launched with the same tag and task")
@@ -280,12 +287,13 @@
         mSplitController.setActivityStackAttributesCalculator(params ->
                 new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
         final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
-                mOverlayContainer1.getOverlayTag(), mActivity);
+                mOverlayContainer1.getOverlayTag(), createMockActivity());
 
         assertWithMessage("overlayContainer1 must be dismissed since the new overlay container"
                 + " is associated with different launching activity")
                 .that(mSplitController.getAllNonFinishingOverlayContainers())
                 .containsExactly(mOverlayContainer2, overlayContainer);
+        assertThat(overlayContainer).isNotEqualTo(mOverlayContainer1);
     }
 
     @Test
@@ -323,7 +331,8 @@
     }
 
     private void createExistingOverlayContainers(boolean visible) {
-        mOverlayContainer1 = createTestOverlayContainer(TASK_ID, "test1", visible);
+        mOverlayContainer1 = createTestOverlayContainer(TASK_ID, "test1", visible,
+                true /* associatedLaunchingActivity */, mActivity);
         mOverlayContainer2 = createTestOverlayContainer(TASK_ID + 1, "test2", visible);
         List<TaskFragmentContainer> overlayContainers = mSplitController
                 .getAllNonFinishingOverlayContainers();
@@ -335,17 +344,49 @@
         mIntent.setComponent(new ComponentName(ApplicationProvider.getApplicationContext(),
                 MinimumDimensionActivity.class));
         final Rect bounds = new Rect(0, 0, 100, 100);
+        final TaskFragmentContainer overlayContainer =
+                createTestOverlayContainer(TASK_ID, "test1");
 
-        SplitPresenter.sanitizeBounds(bounds, SplitPresenter.getMinDimensions(mIntent),
-                TASK_BOUNDS);
+        assertThat(sanitizeBounds(bounds, SplitPresenter.getMinDimensions(mIntent),
+                overlayContainer).isEmpty()).isTrue();
     }
 
     @Test
     public void testSanitizeBounds_notInTaskBounds_expandOverlay() {
         final Rect bounds = new Rect(TASK_BOUNDS);
         bounds.offset(10, 10);
+        final TaskFragmentContainer overlayContainer =
+                createTestOverlayContainer(TASK_ID, "test1");
 
-        SplitPresenter.sanitizeBounds(bounds, null, TASK_BOUNDS);
+        assertThat(sanitizeBounds(bounds, null, overlayContainer)
+                .isEmpty()).isTrue();
+    }
+
+    @Test
+    public void testSanitizeBounds_visibleSplit_expandOverlay() {
+        // Launch a visible split
+        final Activity primaryActivity = createMockActivity();
+        final Activity secondaryActivity = createMockActivity();
+        final TaskFragmentContainer primaryContainer =
+                createMockTaskFragmentContainer(primaryActivity, true /* isVisible */);
+        final TaskFragmentContainer secondaryContainer =
+                createMockTaskFragmentContainer(secondaryActivity, true /* isVisible */);
+
+        final SplitPairRule splitPairRule = createSplitPairRuleBuilder(
+                activityActivityPair -> true /* activityPairPredicate */,
+                activityIntentPair -> true  /* activityIntentPairPredicate */,
+                parentWindowMetrics -> true /* parentWindowMetricsPredicate */)
+                .build();
+        mSplitController.registerSplit(mTransaction, primaryContainer, primaryActivity,
+                secondaryContainer, splitPairRule,  splitPairRule.getDefaultSplitAttributes());
+
+        final Rect bounds = new Rect(0, 0, 100, 100);
+        final TaskFragmentContainer overlayContainer =
+                createTestOverlayContainer(TASK_ID, "test1", true /* isVisible */,
+                        true /* associatedLaunchingActivity */, secondaryActivity);
+
+        assertThat(sanitizeBounds(bounds, null, overlayContainer)
+                .isEmpty()).isTrue();
     }
 
     @Test
@@ -701,6 +742,70 @@
                 .doesNotContain(overlayWithAssociation);
     }
 
+    @Test
+    public void testLaunchPlaceholderIfNecessary_skipIfActivityAssociateOverlay() {
+        setupPlaceholderRule(mActivity);
+        createTestOverlayContainer(TASK_ID, "test", true /* isVisible */,
+                true /* associateLaunchingActivity */, mActivity);
+
+
+        mSplitController.mTransactionManager.startNewTransaction();
+        assertThat(mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity,
+                false /* isOnCreated */)).isFalse();
+
+        verify(mTransaction, never()).startActivityInTaskFragment(any(), any(), any(), any());
+    }
+
+    @Test
+    public void testLaunchPlaceholderIfNecessary_skipIfActivityInOverlay() {
+        setupPlaceholderRule(mActivity);
+        createOrUpdateOverlayTaskFragmentIfNeeded("test1", mActivity);
+
+        mSplitController.mTransactionManager.startNewTransaction();
+        assertThat(mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity,
+                false /* isOnCreated */)).isFalse();
+
+        verify(mTransaction, never()).startActivityInTaskFragment(any(), any(), any(), any());
+    }
+
+    /** Setups a rule to launch placeholder for the given activity. */
+    private void setupPlaceholderRule(@NonNull Activity primaryActivity) {
+        final SplitRule placeholderRule = createSplitPlaceholderRuleBuilder(PLACEHOLDER_INTENT,
+                primaryActivity::equals, i -> false, w -> true)
+                .setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
+                .build();
+        mSplitController.setEmbeddingRules(Collections.singleton(placeholderRule));
+    }
+
+    @Test
+    public void testResolveStartActivityIntent_skipIfAssociateOverlay() {
+        final Intent intent = new Intent();
+        mSplitController.setEmbeddingRules(Collections.singleton(
+                createSplitRule(mActivity, intent)));
+        createTestOverlayContainer(TASK_ID, "test", true /* isVisible */,
+                true /* associateLaunchingActivity */, mActivity);
+        final TaskFragmentContainer container = mSplitController.resolveStartActivityIntent(
+                mTransaction, TASK_ID, intent, mActivity);
+
+        assertThat(container).isNull();
+        verify(mSplitController, never()).resolveStartActivityIntentByRule(any(), anyInt(), any(),
+                any());
+    }
+
+    @Test
+    public void testResolveStartActivityIntent_skipIfLaunchingActivityInOverlay() {
+        final Intent intent = new Intent();
+        mSplitController.setEmbeddingRules(Collections.singleton(
+                createSplitRule(mActivity, intent)));
+        createOrUpdateOverlayTaskFragmentIfNeeded("test1", mActivity);
+        final TaskFragmentContainer container = mSplitController.resolveStartActivityIntent(
+                mTransaction, TASK_ID, intent, mActivity);
+
+        assertThat(container).isNull();
+        verify(mSplitController, never()).resolveStartActivityIntentByRule(any(), anyInt(), any(),
+                any());
+    }
+
     /**
      * A simplified version of {@link SplitController#createOrUpdateOverlayTaskFragmentIfNeeded}
      */
@@ -726,9 +831,16 @@
     /** Creates a mock TaskFragment that has been registered and appeared in the organizer. */
     @NonNull
     private TaskFragmentContainer createMockTaskFragmentContainer(@NonNull Activity activity) {
+        return createMockTaskFragmentContainer(activity, false /* isVisible */);
+    }
+
+    /** Creates a mock TaskFragment that has been registered and appeared in the organizer. */
+    @NonNull
+    private TaskFragmentContainer createMockTaskFragmentContainer(
+            @NonNull Activity activity, boolean isVisible) {
         final TaskFragmentContainer container = mSplitController.newContainer(activity,
                 activity.getTaskId());
-        setupTaskFragmentInfo(container, activity, false /* isVisible */);
+        setupTaskFragmentInfo(container, activity, isVisible);
         return container;
     }
 
@@ -745,17 +857,26 @@
                 true /* associateLaunchingActivity */);
     }
 
+    @NonNull
+    private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag,
+                boolean isVisible, boolean associateLaunchingActivity) {
+        return createTestOverlayContainer(taskId, tag, isVisible, associateLaunchingActivity,
+                null /* launchingActivity */);
+    }
+
     // TODO(b/243518738): add more test coverage on overlay container without activity association
     //  once we have use cases.
     @NonNull
     private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag,
-                boolean isVisible, boolean associateLaunchingActivity) {
-        Activity activity = createMockActivity();
+            boolean isVisible, boolean associateLaunchingActivity,
+            @Nullable Activity launchingActivity) {
+        final Activity activity = launchingActivity != null
+                ? launchingActivity : createMockActivity();
         TaskFragmentContainer overlayContainer = mSplitController.newContainer(
                 null /* pendingAppearedActivity */, mIntent, activity, taskId,
                 null /* pairedPrimaryContainer */, tag, Bundle.EMPTY,
                 associateLaunchingActivity);
-        setupTaskFragmentInfo(overlayContainer, activity, isVisible);
+        setupTaskFragmentInfo(overlayContainer, createMockActivity(), isVisible);
         return overlayContainer;
     }
 
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
index abfc9c8..44ab2c4 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
@@ -538,9 +538,7 @@
         // container1.
         container2.setInfo(mTransaction, mInfo);
 
-        assertTrue(container1.hasActivity(mActivity.getActivityToken()));
-        assertFalse(container2.hasActivity(mActivity.getActivityToken()));
-
+        assertTrue(container2.hasActivity(mActivity.getActivityToken()));
         // When the pending appeared record is removed from container1, we respect the appeared
         // record in container2.
         container1.removePendingAppearedActivity(mActivity.getActivityToken());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 4eff3f0..6e61f22 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -26,6 +26,7 @@
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipDisplayLayoutState;
@@ -43,6 +44,7 @@
 import com.android.wm.shell.pip2.phone.PipScheduler;
 import com.android.wm.shell.pip2.phone.PipTouchHandler;
 import com.android.wm.shell.pip2.phone.PipTransition;
+import com.android.wm.shell.pip2.phone.PipTransitionState;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
@@ -69,9 +71,11 @@
             PipBoundsAlgorithm pipBoundsAlgorithm,
             Optional<PipController> pipController,
             PipTouchHandler pipTouchHandler,
-            @NonNull PipScheduler pipScheduler) {
+            @NonNull PipScheduler pipScheduler,
+            @NonNull PipTransitionState pipStackListenerController) {
         return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
-                pipBoundsState, null, pipBoundsAlgorithm, pipScheduler);
+                pipBoundsState, null, pipBoundsAlgorithm, pipScheduler,
+                pipStackListenerController);
     }
 
     @WMSingleton
@@ -85,6 +89,9 @@
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PipDisplayLayoutState pipDisplayLayoutState,
             PipScheduler pipScheduler,
+            TaskStackListenerImpl taskStackListener,
+            ShellTaskOrganizer shellTaskOrganizer,
+            PipTransitionState pipTransitionState,
             @ShellMainThread ShellExecutor mainExecutor) {
         if (!PipUtils.isPip2ExperimentEnabled()) {
             return Optional.empty();
@@ -92,7 +99,7 @@
             return Optional.ofNullable(PipController.create(
                     context, shellInit, shellController, displayController, displayInsetsController,
                     pipBoundsState, pipBoundsAlgorithm, pipDisplayLayoutState, pipScheduler,
-                    mainExecutor));
+                    taskStackListener, shellTaskOrganizer, pipTransitionState, mainExecutor));
         }
     }
 
@@ -101,8 +108,8 @@
     static PipScheduler providePipScheduler(Context context,
             PipBoundsState pipBoundsState,
             @ShellMainThread ShellExecutor mainExecutor,
-            ShellTaskOrganizer shellTaskOrganizer) {
-        return new PipScheduler(context, pipBoundsState, mainExecutor, shellTaskOrganizer);
+            PipTransitionState pipTransitionState) {
+        return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState);
     }
 
     @WMSingleton
@@ -146,4 +153,10 @@
         return new PipMotionHelper(context, pipBoundsState, menuController, pipSnapAlgorithm,
                 floatingContentCoordinator, pipPerfHintControllerOptional);
     }
+
+    @WMSingleton
+    @Provides
+    static PipTransitionState providePipStackListenerController() {
+        return new PipTransitionState();
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 4f71a02..7730285 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -54,7 +54,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.function.Consumer;
 
 /**
  * Responsible supplying PiP Transitions.
@@ -125,12 +124,8 @@
 
     /**
      * Called when the Shell wants to start resizing Pip transition/animation.
-     *
-     * @param onFinishResizeCallback callback guaranteed to execute when animation ends and
-     *                               client completes any potential draws upon WM state updates.
      */
-    public void startResizeTransition(WindowContainerTransaction wct,
-            Consumer<Rect> onFinishResizeCallback) {
+    public void startResizeTransition(WindowContainerTransaction wct) {
         // Default implementation does nothing.
     }
 
@@ -266,9 +261,9 @@
     }
 
     /** Whether a particular package is same as current pip package. */
-    public boolean isInPipPackage(String packageName) {
+    public boolean isPackageActiveInPip(String packageName) {
         final TaskInfo inPipTask = mPipOrganizer.getTaskInfo();
-        return packageName != null && inPipTask != null
+        return packageName != null && inPipTask != null && mPipOrganizer.isInPip()
                 && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent));
     }
 
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 1e18b8c..a12882f 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
@@ -16,25 +16,31 @@
 
 package com.android.wm.shell.pip2.phone;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 
 import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;
 
+import android.app.ActivityManager;
 import android.app.PictureInPictureParams;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.view.InsetsState;
 import android.view.SurfaceControl;
 
 import androidx.annotation.BinderThread;
+import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.Preconditions;
 import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.DisplayLayout;
@@ -42,6 +48,8 @@
 import com.android.wm.shell.common.RemoteCallable;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SingleInstanceRemoteListener;
+import com.android.wm.shell.common.TaskStackListenerCallback;
+import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.common.pip.IPip;
 import com.android.wm.shell.common.pip.IPipAnimationListener;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
@@ -57,8 +65,11 @@
  * Manages the picture-in-picture (PIP) UI and states for Phones.
  */
 public class PipController implements ConfigurationChangeListener,
+        PipTransitionState.PipTransitionStateChangedListener,
         DisplayController.OnDisplaysChangedListener, RemoteCallable<PipController> {
     private static final String TAG = PipController.class.getSimpleName();
+    private static final String SWIPE_TO_PIP_APP_BOUNDS = "pip_app_bounds";
+    private static final String SWIPE_TO_PIP_OVERLAY = "swipe_to_pip_overlay";
 
     private final Context mContext;
     private final ShellController mShellController;
@@ -68,6 +79,9 @@
     private final PipBoundsAlgorithm mPipBoundsAlgorithm;
     private final PipDisplayLayoutState mPipDisplayLayoutState;
     private final PipScheduler mPipScheduler;
+    private final TaskStackListenerImpl mTaskStackListener;
+    private final ShellTaskOrganizer mShellTaskOrganizer;
+    private final PipTransitionState mPipTransitionState;
     private final ShellExecutor mMainExecutor;
 
     // Wrapper for making Binder calls into PiP animation listener hosted in launcher's Recents.
@@ -104,6 +118,9 @@
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PipDisplayLayoutState pipDisplayLayoutState,
             PipScheduler pipScheduler,
+            TaskStackListenerImpl taskStackListener,
+            ShellTaskOrganizer shellTaskOrganizer,
+            PipTransitionState pipTransitionState,
             ShellExecutor mainExecutor) {
         mContext = context;
         mShellController = shellController;
@@ -113,6 +130,10 @@
         mPipBoundsAlgorithm = pipBoundsAlgorithm;
         mPipDisplayLayoutState = pipDisplayLayoutState;
         mPipScheduler = pipScheduler;
+        mTaskStackListener = taskStackListener;
+        mShellTaskOrganizer = shellTaskOrganizer;
+        mPipTransitionState = pipTransitionState;
+        mPipTransitionState.addPipTransitionStateChangedListener(this);
         mMainExecutor = mainExecutor;
 
         if (PipUtils.isPip2ExperimentEnabled()) {
@@ -132,6 +153,9 @@
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PipDisplayLayoutState pipDisplayLayoutState,
             PipScheduler pipScheduler,
+            TaskStackListenerImpl taskStackListener,
+            ShellTaskOrganizer shellTaskOrganizer,
+            PipTransitionState pipTransitionState,
             ShellExecutor mainExecutor) {
         if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
             ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -140,7 +164,8 @@
         }
         return new PipController(context, shellInit, shellController, displayController,
                 displayInsetsController, pipBoundsState, pipBoundsAlgorithm, pipDisplayLayoutState,
-                pipScheduler, mainExecutor);
+                pipScheduler, taskStackListener, shellTaskOrganizer, pipTransitionState,
+                mainExecutor);
     }
 
     private void onInit() {
@@ -164,6 +189,17 @@
         mShellController.addExternalInterface(KEY_EXTRA_SHELL_PIP,
                 this::createExternalInterface, this);
         mShellController.addConfigurationChangeListener(this);
+
+        mTaskStackListener.addListener(new TaskStackListenerCallback() {
+            @Override
+            public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+                    boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
+                if (task.getWindowingMode() != WINDOWING_MODE_PINNED) {
+                    return;
+                }
+                mPipScheduler.scheduleExitPipViaExpand();
+            }
+        });
     }
 
     private ExternalInterfaceBinder createExternalInterface() {
@@ -245,11 +281,46 @@
             Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                 "onSwipePipToHomeAnimationStart: %s", componentName);
-        mPipScheduler.onSwipePipToHomeAnimationStart(taskId, componentName, destinationBounds,
-                overlay, appBounds);
+        Bundle extra = new Bundle();
+        extra.putParcelable(SWIPE_TO_PIP_OVERLAY, overlay);
+        extra.putParcelable(SWIPE_TO_PIP_APP_BOUNDS, appBounds);
+        mPipTransitionState.setState(PipTransitionState.SWIPING_TO_PIP, extra);
+        if (overlay != null) {
+            // Shell transitions might use a root animation leash, which will be removed when
+            // the Recents transition is finished. Launcher attaches the overlay leash to this
+            // animation target leash; thus, we need to reparent it to the actual Task surface now.
+            // PipTransition is responsible to fade it out and cleanup when finishing the enter PIP
+            // transition.
+            SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+            mShellTaskOrganizer.reparentChildSurfaceToTask(taskId, overlay, tx);
+            tx.setLayer(overlay, Integer.MAX_VALUE);
+            tx.apply();
+        }
         mPipRecentsAnimationListener.onPipAnimationStarted();
     }
 
+    @Override
+    public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
+            @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) {
+        if (newState == PipTransitionState.SWIPING_TO_PIP) {
+            Preconditions.checkState(extra != null,
+                    "No extra bundle for " + mPipTransitionState);
+
+            SurfaceControl overlay = extra.getParcelable(
+                    SWIPE_TO_PIP_OVERLAY, SurfaceControl.class);
+            Rect appBounds = extra.getParcelable(
+                    SWIPE_TO_PIP_APP_BOUNDS, Rect.class);
+
+            Preconditions.checkState(appBounds != null,
+                    "App bounds can't be null for " + mPipTransitionState);
+            mPipTransitionState.setSwipePipToHomeState(overlay, appBounds);
+        } else if (newState == PipTransitionState.ENTERED_PIP) {
+            if (mPipTransitionState.isInSwipePipToHomeTransition()) {
+                mPipTransitionState.resetSwipePipToHomeState();
+            }
+        }
+    }
+
     //
     // IPipAnimationListener Binder proxy helpers
     //
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index b4ca7df..72fa3ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -21,21 +21,16 @@
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
 
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.graphics.Rect;
-import android.view.SurfaceControl;
-import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
 import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.core.content.ContextCompat;
 
-import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipUtils;
@@ -43,7 +38,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.function.Consumer;
 
 /**
  * Scheduler for Shell initiated PiP transitions and animations.
@@ -55,31 +49,10 @@
     private final Context mContext;
     private final PipBoundsState mPipBoundsState;
     private final ShellExecutor mMainExecutor;
-    private final ShellTaskOrganizer mShellTaskOrganizer;
+    private final PipTransitionState mPipTransitionState;
     private PipSchedulerReceiver mSchedulerReceiver;
     private PipTransitionController mPipTransitionController;
 
-    // pinned PiP task's WC token
-    @Nullable
-    private WindowContainerToken mPipTaskToken;
-
-    // pinned PiP task's leash
-    @Nullable
-    private SurfaceControl mPinnedTaskLeash;
-
-    // true if Launcher has started swipe PiP to home animation
-    private boolean mInSwipePipToHomeTransition;
-
-    // Overlay leash potentially used during swipe PiP to home transition;
-    // if null while mInSwipePipToHomeTransition is true, then srcRectHint was invalid.
-    @Nullable
-    SurfaceControl mSwipePipToHomeOverlay;
-
-    // App bounds used when as a starting point to swipe PiP to home animation in Launcher;
-    // these are also used to calculate the app icon overlay buffer size.
-    @NonNull
-    final Rect mSwipePipToHomeAppBounds = new Rect();
-
     /**
      * Temporary PiP CUJ codes to schedule PiP related transitions directly from Shell.
      * This is used for a broadcast receiver to resolve intents. This should be removed once
@@ -118,11 +91,11 @@
     public PipScheduler(Context context,
             PipBoundsState pipBoundsState,
             ShellExecutor mainExecutor,
-            ShellTaskOrganizer shellTaskOrganizer) {
+            PipTransitionState pipTransitionState) {
         mContext = context;
         mPipBoundsState = pipBoundsState;
         mMainExecutor = mainExecutor;
-        mShellTaskOrganizer = shellTaskOrganizer;
+        mPipTransitionState = pipTransitionState;
 
         if (PipUtils.isPip2ExperimentEnabled()) {
             // temporary broadcast receiver to initiate exit PiP via expand
@@ -140,25 +113,17 @@
         mPipTransitionController = pipTransitionController;
     }
 
-    void setPinnedTaskLeash(SurfaceControl pinnedTaskLeash) {
-        mPinnedTaskLeash = pinnedTaskLeash;
-    }
-
-    void setPipTaskToken(@Nullable WindowContainerToken pipTaskToken) {
-        mPipTaskToken = pipTaskToken;
-    }
-
     @Nullable
     private WindowContainerTransaction getExitPipViaExpandTransaction() {
-        if (mPipTaskToken == null) {
+        if (mPipTransitionState.mPipTaskToken == null) {
             return null;
         }
         WindowContainerTransaction wct = new WindowContainerTransaction();
         // final expanded bounds to be inherited from the parent
-        wct.setBounds(mPipTaskToken, null);
+        wct.setBounds(mPipTransitionState.mPipTaskToken, null);
         // if we are hitting a multi-activity case
         // windowing mode change will reparent to original host task
-        wct.setWindowingMode(mPipTaskToken, WINDOWING_MODE_UNDEFINED);
+        wct.setWindowingMode(mPipTransitionState.mPipTaskToken, WINDOWING_MODE_UNDEFINED);
         return wct;
     }
 
@@ -183,43 +148,12 @@
     /**
      * Animates resizing of the pinned stack given the duration.
      */
-    public void scheduleAnimateResizePip(Rect toBounds, Consumer<Rect> onFinishResizeCallback) {
-        if (mPipTaskToken == null) {
+    public void scheduleAnimateResizePip(Rect toBounds) {
+        if (mPipTransitionState.mPipTaskToken == null || !mPipTransitionState.isInPip()) {
             return;
         }
         WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.setBounds(mPipTaskToken, toBounds);
-        mPipTransitionController.startResizeTransition(wct, onFinishResizeCallback);
-    }
-
-    void onSwipePipToHomeAnimationStart(int taskId, ComponentName componentName,
-            Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
-        mInSwipePipToHomeTransition = true;
-        mSwipePipToHomeOverlay = overlay;
-        mSwipePipToHomeAppBounds.set(appBounds);
-        if (overlay != null) {
-            // Shell transitions might use a root animation leash, which will be removed when
-            // the Recents transition is finished. Launcher attaches the overlay leash to this
-            // animation target leash; thus, we need to reparent it to the actual Task surface now.
-            // PipTransition is responsible to fade it out and cleanup when finishing the enter PIP
-            // transition.
-            SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
-            mShellTaskOrganizer.reparentChildSurfaceToTask(taskId, overlay, tx);
-            tx.setLayer(overlay, Integer.MAX_VALUE);
-            tx.apply();
-        }
-    }
-
-    void setInSwipePipToHomeTransition(boolean inSwipePipToHome) {
-        mInSwipePipToHomeTransition = inSwipePipToHome;
-    }
-
-    boolean isInSwipePipToHomeTransition() {
-        return mInSwipePipToHomeTransition;
-    }
-
-    void onExitPip() {
-        mPipTaskToken = null;
-        mPinnedTaskLeash = null;
+        wct.setBounds(mPipTransitionState.mPipTaskToken, toBounds);
+        mPipTransitionController.startResizeTransition(wct);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index e829d4e..12dce5b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -34,6 +34,7 @@
 import android.app.PictureInPictureParams;
 import android.content.Context;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
@@ -43,6 +44,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.internal.util.Preconditions;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
@@ -54,23 +56,33 @@
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 
-import java.util.function.Consumer;
-
 /**
  * Implementation of transitions for PiP on phone.
  */
-public class PipTransition extends PipTransitionController {
+public class PipTransition extends PipTransitionController implements
+        PipTransitionState.PipTransitionStateChangedListener {
     private static final String TAG = PipTransition.class.getSimpleName();
+    private static final String PIP_TASK_TOKEN = "pip_task_token";
+    private static final String PIP_TASK_LEASH = "pip_task_leash";
+
     /**
      * The fixed start delay in ms when fading out the content overlay from bounds animation.
      * The fadeout animation is guaranteed to start after the client has drawn under the new config.
      */
     private static final int CONTENT_OVERLAY_FADE_OUT_DELAY_MS = 400;
 
+    //
+    // Dependencies
+    //
+
     private final Context mContext;
     private final PipScheduler mPipScheduler;
-    @Nullable
-    private WindowContainerToken mPipTaskToken;
+    private final PipTransitionState mPipTransitionState;
+
+    //
+    // Transition tokens
+    //
+
     @Nullable
     private IBinder mEnterTransition;
     @Nullable
@@ -78,7 +90,14 @@
     @Nullable
     private IBinder mResizeTransition;
 
-    private Consumer<Rect> mFinishResizeCallback;
+    //
+    // Internal state and relevant cached info
+    //
+
+    @Nullable
+    private WindowContainerToken mPipTaskToken;
+    @Nullable
+    private SurfaceControl mPipLeash;
 
     public PipTransition(
             Context context,
@@ -88,13 +107,16 @@
             PipBoundsState pipBoundsState,
             PipMenuController pipMenuController,
             PipBoundsAlgorithm pipBoundsAlgorithm,
-            PipScheduler pipScheduler) {
+            PipScheduler pipScheduler,
+            PipTransitionState pipTransitionState) {
         super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
                 pipBoundsAlgorithm);
 
         mContext = context;
         mPipScheduler = pipScheduler;
         mPipScheduler.setPipTransitionController(this);
+        mPipTransitionState = pipTransitionState;
+        mPipTransitionState.addPipTransitionStateChangedListener(this);
     }
 
     @Override
@@ -104,6 +126,10 @@
         }
     }
 
+    //
+    // Transition collection stage lifecycle hooks
+    //
+
     @Override
     public void startExitTransition(int type, WindowContainerTransaction out,
             @Nullable Rect destinationBounds) {
@@ -117,13 +143,11 @@
     }
 
     @Override
-    public void startResizeTransition(WindowContainerTransaction wct,
-            Consumer<Rect> onFinishResizeCallback) {
+    public void startResizeTransition(WindowContainerTransaction wct) {
         if (wct == null) {
             return;
         }
         mResizeTransition = mTransitions.startTransition(TRANSIT_RESIZE_PIP, wct, this);
-        mFinishResizeCallback = onFinishResizeCallback;
     }
 
     @Nullable
@@ -146,6 +170,10 @@
         }
     }
 
+    //
+    // Transition playing stage lifecycle hooks
+    //
+
     @Override
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@@ -163,7 +191,19 @@
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         if (transition == mEnterTransition || info.getType() == TRANSIT_PIP) {
             mEnterTransition = null;
-            if (mPipScheduler.isInSwipePipToHomeTransition()) {
+            // If we are in swipe PiP to Home transition we are ENTERING_PIP as a jumpcut transition
+            // is being carried out.
+            TransitionInfo.Change pipChange = getPipChange(info);
+
+            // If there is no PiP change, exit this transition handler and potentially try others.
+            if (pipChange == null) return false;
+
+            Bundle extra = new Bundle();
+            extra.putParcelable(PIP_TASK_TOKEN, pipChange.getContainer());
+            extra.putParcelable(PIP_TASK_LEASH, pipChange.getLeash());
+            mPipTransitionState.setState(PipTransitionState.ENTERING_PIP, extra);
+
+            if (mPipTransitionState.isInSwipePipToHomeTransition()) {
                 // If this is the second transition as a part of swipe PiP to home cuj,
                 // handle this transition as a special case with no-op animation.
                 return handleSwipePipToHomeTransition(info, startTransaction, finishTransaction,
@@ -179,9 +219,11 @@
                     finishCallback);
         } else if (transition == mExitViaExpandTransition) {
             mExitViaExpandTransition = null;
+            mPipTransitionState.setState(PipTransitionState.EXITING_PIP);
             return startExpandAnimation(info, startTransaction, finishTransaction, finishCallback);
         } else if (transition == mResizeTransition) {
             mResizeTransition = null;
+            mPipTransitionState.setState(PipTransitionState.CHANGING_PIP_BOUNDS);
             return startResizeAnimation(info, startTransaction, finishTransaction, finishCallback);
         }
 
@@ -191,6 +233,10 @@
         return false;
     }
 
+    //
+    // Animation schedulers and entry points
+    //
+
     private boolean startResizeAnimation(@NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
@@ -236,11 +282,7 @@
         if (pipChange == null) {
             return false;
         }
-        mPipScheduler.setInSwipePipToHomeTransition(false);
-        mPipTaskToken = pipChange.getContainer();
-
-        // cache the PiP task token and leash
-        mPipScheduler.setPipTaskToken(mPipTaskToken);
+        WindowContainerToken pipTaskToken = pipChange.getContainer();
         SurfaceControl pipLeash = pipChange.getLeash();
 
         PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams;
@@ -264,9 +306,9 @@
         } else {
             final float scaleX = (float) destinationBounds.width() / startBounds.width();
             final float scaleY = (float) destinationBounds.height() / startBounds.height();
-            final int overlaySize = PipContentOverlay.PipAppIconOverlay
-                    .getOverlaySize(mPipScheduler.mSwipePipToHomeAppBounds, destinationBounds);
-            SurfaceControl overlayLeash = mPipScheduler.mSwipePipToHomeOverlay;
+            final int overlaySize = PipContentOverlay.PipAppIconOverlay.getOverlaySize(
+                    mPipTransitionState.getSwipePipToHomeAppBounds(), destinationBounds);
+            SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay();
 
             startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top)
                     .setScale(pipLeash, scaleX, scaleY)
@@ -274,7 +316,7 @@
                     .reparent(overlayLeash, pipLeash)
                     .setLayer(overlayLeash, Integer.MAX_VALUE);
 
-            if (mPipTaskToken != null) {
+            if (pipTaskToken != null) {
                 SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
                 tx.addTransactionCommittedListener(mPipScheduler.getMainExecutor(),
                                 this::onClientDrawAtTransitionEnd)
@@ -282,7 +324,7 @@
                         .setPosition(overlayLeash,
                                 (destinationBounds.width() - overlaySize) / 2f,
                                 (destinationBounds.height() - overlaySize) / 2f);
-                finishWct.setBoundsChangeTransaction(mPipTaskToken, tx);
+                finishWct.setBoundsChangeTransaction(pipTaskToken, tx);
             }
         }
         startTransaction.apply();
@@ -293,14 +335,6 @@
         return true;
     }
 
-    private void onClientDrawAtTransitionEnd() {
-        startOverlayFadeoutAnimation();
-    }
-
-    //
-    // Subroutines setting up and starting transitions' animations.
-    //
-
     private void startOverlayFadeoutAnimation() {
         ValueAnimator animator = ValueAnimator.ofFloat(1f, 0f);
         animator.setDuration(CONTENT_OVERLAY_FADE_OUT_DELAY_MS);
@@ -309,15 +343,17 @@
             public void onAnimationEnd(Animator animation) {
                 super.onAnimationEnd(animation);
                 SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
-                tx.remove(mPipScheduler.mSwipePipToHomeOverlay);
+                tx.remove(mPipTransitionState.getSwipePipToHomeOverlay());
                 tx.apply();
-                mPipScheduler.mSwipePipToHomeOverlay = null;
+
+                // We have fully completed enter-PiP animation after the overlay is gone.
+                mPipTransitionState.setState(PipTransitionState.ENTERED_PIP);
             }
         });
         animator.addUpdateListener(animation -> {
             float alpha = (float) animation.getAnimatedValue();
             SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
-            tx.setAlpha(mPipScheduler.mSwipePipToHomeOverlay, alpha).apply();
+            tx.setAlpha(mPipTransitionState.getSwipePipToHomeOverlay(), alpha).apply();
         });
         animator.start();
     }
@@ -330,10 +366,8 @@
         if (pipChange == null) {
             return false;
         }
-        mPipTaskToken = pipChange.getContainer();
-
         // cache the PiP task token and leash
-        mPipScheduler.setPipTaskToken(mPipTaskToken);
+        WindowContainerToken pipTaskToken = pipChange.getContainer();
 
         startTransaction.apply();
         // TODO: b/275910498 Use a new implementation of the PiP animator here.
@@ -349,10 +383,8 @@
         if (pipChange == null) {
             return false;
         }
-        mPipTaskToken = pipChange.getContainer();
-
         // cache the PiP task token and leash
-        mPipScheduler.setPipTaskToken(mPipTaskToken);
+        WindowContainerToken pipTaskToken = pipChange.getContainer();
 
         startTransaction.apply();
         finishCallback.onTransitionFinished(null);
@@ -366,7 +398,7 @@
         startTransaction.apply();
         // TODO: b/275910498 Use a new implementation of the PiP animator here.
         finishCallback.onTransitionFinished(null);
-        onExitPip();
+        mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
         return true;
     }
 
@@ -376,12 +408,20 @@
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         startTransaction.apply();
         finishCallback.onTransitionFinished(null);
-        onExitPip();
+        mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
         return true;
     }
 
+    /**
+     * TODO: b/275910498 Use a new implementation of the PiP animator here.
+     */
+    private void startResizeAnimation(SurfaceControl leash, Rect startBounds,
+            Rect endBounds, int duration) {
+        mPipTransitionState.setState(PipTransitionState.CHANGED_PIP_BOUNDS);
+    }
+
     //
-    // Utility methods for checking PiP-related transition info and requests.
+    // Various helpers to resolve transition requests and infos
     //
 
     @Nullable
@@ -442,11 +482,11 @@
     }
 
     private boolean isRemovePipTransition(@NonNull TransitionInfo info) {
-        if (mPipTaskToken == null) {
+        if (mPipTransitionState.mPipTaskToken == null) {
             // PiP removal makes sense if enter-PiP has cached a valid pinned task token.
             return false;
         }
-        TransitionInfo.Change pipChange = info.getChange(mPipTaskToken);
+        TransitionInfo.Change pipChange = info.getChange(mPipTransitionState.mPipTaskToken);
         if (pipChange == null) {
             // Search for the PiP change by token since the windowing mode might be FULLSCREEN now.
             return false;
@@ -460,14 +500,43 @@
         return isPipMovedToBack || isPipClosed;
     }
 
-    /**
-     * TODO: b/275910498 Use a new implementation of the PiP animator here.
-     */
-    private void startResizeAnimation(SurfaceControl leash, Rect startBounds,
-            Rect endBounds, int duration) {}
+    //
+    // Miscellaneous callbacks and listeners
+    //
 
-    private void onExitPip() {
-        mPipTaskToken = null;
-        mPipScheduler.onExitPip();
+    private void onClientDrawAtTransitionEnd() {
+        if (mPipTransitionState.getSwipePipToHomeOverlay() != null) {
+            startOverlayFadeoutAnimation();
+        } else if (mPipTransitionState.getState() == PipTransitionState.ENTERING_PIP) {
+            // If we were entering PiP (i.e. playing the animation) with a valid srcRectHint,
+            // and then we get a signal on client finishing its draw after the transition
+            // has ended, then we have fully entered PiP.
+            mPipTransitionState.setState(PipTransitionState.ENTERED_PIP);
+        }
+    }
+
+    @Override
+    public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
+            @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) {
+        switch (newState) {
+            case PipTransitionState.ENTERING_PIP:
+                Preconditions.checkState(extra != null,
+                        "No extra bundle for " + mPipTransitionState);
+
+                mPipTransitionState.mPipTaskToken = extra.getParcelable(
+                        PIP_TASK_TOKEN, WindowContainerToken.class);
+                mPipTransitionState.mPinnedTaskLeash = extra.getParcelable(
+                        PIP_TASK_LEASH, SurfaceControl.class);
+                boolean hasValidTokenAndLeash = mPipTransitionState.mPipTaskToken != null
+                        && mPipTransitionState.mPinnedTaskLeash != null;
+
+                Preconditions.checkState(hasValidTokenAndLeash,
+                        "Unexpected bundle for " + mPipTransitionState);
+                break;
+            case PipTransitionState.EXITED_PIP:
+                mPipTransitionState.mPipTaskToken = null;
+                mPipTransitionState.mPinnedTaskLeash = null;
+                break;
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
new file mode 100644
index 0000000..f7bc622
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
@@ -0,0 +1,275 @@
+/*
+ * 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.wm.shell.pip2.phone;
+
+import android.annotation.IntDef;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains the state relevant to carry out or probe the status of PiP transitions.
+ *
+ * <p>Existing and new PiP components can subscribe to PiP transition related state changes
+ * via <code>PipTransitionStateChangedListener</code>.</p>
+ *
+ * <p><code>PipTransitionState</code> users shouldn't rely on listener execution ordering.
+ * For example, if a class <code>Foo</code> wants to change some arbitrary state A that belongs
+ * to some other class <code>Bar</code>, a special care must be given when manipulating state A in
+ * <code>Foo#onPipTransitionStateChanged()</code>, since that's the responsibility of
+ * the class <code>Bar</code>.</p>
+ *
+ * <p>Hence, the recommended usage for classes who want to subscribe to
+ * <code>PipTransitionState</code> changes is to manipulate only their own internal state or
+ * <code>PipTransitionState</code> state.</p>
+ *
+ * <p>If there is some state that must be manipulated in another class <code>Bar</code>, it should
+ * just be moved to <code>PipTransitionState</code> and become a shared state
+ * between Foo and Bar.</p>
+ *
+ * <p>Moreover, <code>onPipTransitionStateChanged(oldState, newState, extra)</code>
+ * receives a <code>Bundle</code> extra object that can be optionally set via
+ * <code>setState(state, extra)</code>. This can be used to resolve extra information to update
+ * relevant internal or <code>PipTransitionState</code> state. However, each listener
+ * needs to check for whether the extra passed is correct for a particular state,
+ * and throw an <code>IllegalStateException</code> otherwise.</p>
+ */
+public class PipTransitionState {
+    public static final int UNDEFINED = 0;
+
+    // State for Launcher animating the swipe PiP to home animation.
+    public static final int SWIPING_TO_PIP = 1;
+
+    // State for Shell animating enter PiP or jump-cutting to PiP mode after Launcher animation.
+    public static final int ENTERING_PIP = 2;
+
+    // State for app finishing drawing in PiP mode as a final step in enter PiP flow.
+    public static final int ENTERED_PIP = 3;
+
+    // State for scheduling a transition to change PiP bounds.
+    public static final int CHANGING_PIP_BOUNDS = 4;
+
+    // State for app potentially finishing drawing in new PiP bounds after resize is complete.
+    public static final int CHANGED_PIP_BOUNDS = 5;
+
+    // State for starting exiting PiP.
+    public static final int EXITING_PIP = 6;
+
+    // State for finishing exit PiP flow.
+    public static final int EXITED_PIP = 7;
+
+    private static final int FIRST_CUSTOM_STATE = 1000;
+
+    private int mPrevCustomState = FIRST_CUSTOM_STATE;
+
+    @IntDef(prefix = { "TRANSITION_STATE_" }, value =  {
+            UNDEFINED,
+            SWIPING_TO_PIP,
+            ENTERING_PIP,
+            ENTERED_PIP,
+            CHANGING_PIP_BOUNDS,
+            CHANGED_PIP_BOUNDS,
+            EXITING_PIP,
+            EXITED_PIP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TransitionState {}
+
+    @TransitionState
+    private int mState;
+
+    //
+    // Swipe up to enter PiP related state
+    //
+
+    // true if Launcher has started swipe PiP to home animation
+    private boolean mInSwipePipToHomeTransition;
+
+    // App bounds used when as a starting point to swipe PiP to home animation in Launcher;
+    // these are also used to calculate the app icon overlay buffer size.
+    @NonNull
+    private final Rect mSwipePipToHomeAppBounds = new Rect();
+
+    //
+    // Tokens and leashes
+    //
+
+    // pinned PiP task's WC token
+    @Nullable
+    WindowContainerToken mPipTaskToken;
+
+    // pinned PiP task's leash
+    @Nullable
+    SurfaceControl mPinnedTaskLeash;
+
+    // Overlay leash potentially used during swipe PiP to home transition;
+    // if null while mInSwipePipToHomeTransition is true, then srcRectHint was invalid.
+    @Nullable
+    private SurfaceControl mSwipePipToHomeOverlay;
+
+    /**
+     * An interface to track state updates as we progress through PiP transitions.
+     */
+    public interface PipTransitionStateChangedListener {
+
+        /** Reports changes in PiP transition state. */
+        void onPipTransitionStateChanged(@TransitionState int oldState,
+                @TransitionState int newState, @Nullable Bundle extra);
+    }
+
+    private final List<PipTransitionStateChangedListener> mCallbacks = new ArrayList<>();
+
+    /**
+     * @return the state of PiP in the context of transitions.
+     */
+    @TransitionState
+    public int getState() {
+        return mState;
+    }
+
+    /**
+     * Sets the state of PiP in the context of transitions.
+     */
+    public void setState(@TransitionState int state) {
+        setState(state, null /* extra */);
+    }
+
+    /**
+     * Sets the state of PiP in the context of transitions
+     *
+     * @param extra a bundle passed to the subscribed listeners to resolve/cache extra info.
+     */
+    public void setState(@TransitionState int state, @Nullable Bundle extra) {
+        if (state == ENTERING_PIP || state == SWIPING_TO_PIP) {
+            // Whenever we are entering PiP caller must provide extra state to set as well.
+            Preconditions.checkArgument(extra != null && !extra.isEmpty(),
+                    "No extra bundle for either ENTERING_PIP or SWIPING_TO_PIP state.");
+        }
+        if (mState != state) {
+            dispatchPipTransitionStateChanged(mState, state, extra);
+            mState = state;
+        }
+    }
+
+    private void dispatchPipTransitionStateChanged(@TransitionState int oldState,
+            @TransitionState int newState, @Nullable Bundle extra) {
+        mCallbacks.forEach(l -> l.onPipTransitionStateChanged(oldState, newState, extra));
+    }
+
+    /**
+     * Adds a {@link PipTransitionStateChangedListener} for future PiP transition state updates.
+     */
+    public void addPipTransitionStateChangedListener(PipTransitionStateChangedListener listener) {
+        if (mCallbacks.contains(listener)) {
+            return;
+        }
+        mCallbacks.add(listener);
+    }
+
+    /**
+     * @return true if provided {@link PipTransitionStateChangedListener}
+     * is registered before removing it.
+     */
+    public boolean removePipTransitionStateChangedListener(
+            PipTransitionStateChangedListener listener) {
+        return mCallbacks.remove(listener);
+    }
+
+    /**
+     * @return true if we have fully entered PiP.
+     */
+    public boolean isInPip() {
+        return mState > ENTERING_PIP && mState < EXITING_PIP;
+    }
+
+    void setSwipePipToHomeState(@Nullable SurfaceControl overlayLeash,
+            @NonNull Rect appBounds) {
+        mInSwipePipToHomeTransition = true;
+        if (overlayLeash != null && !appBounds.isEmpty()) {
+            mSwipePipToHomeOverlay = overlayLeash;
+            mSwipePipToHomeAppBounds.set(appBounds);
+        }
+    }
+
+    void resetSwipePipToHomeState() {
+        mInSwipePipToHomeTransition = false;
+        mSwipePipToHomeOverlay = null;
+        mSwipePipToHomeAppBounds.setEmpty();
+    }
+
+    /**
+     * @return true if in swipe PiP to home. Note that this is true until overlay fades if used too.
+     */
+    public boolean isInSwipePipToHomeTransition() {
+        return mInSwipePipToHomeTransition;
+    }
+
+    /**
+     * @return the overlay used during swipe PiP to home for invalid srcRectHints in auto-enter PiP;
+     * null if srcRectHint provided is valid.
+     */
+    @Nullable
+    public SurfaceControl getSwipePipToHomeOverlay() {
+        return mSwipePipToHomeOverlay;
+    }
+
+    /**
+     * @return app bounds used to calculate
+     */
+    @NonNull
+    public Rect getSwipePipToHomeAppBounds() {
+        return mSwipePipToHomeAppBounds;
+    }
+
+    /**
+     * @return a custom state solely for internal use by the caller.
+     */
+    @TransitionState
+    public int getCustomState() {
+        return ++mPrevCustomState;
+    }
+
+    private String stateToString() {
+        switch (mState) {
+            case UNDEFINED: return "undefined";
+            case ENTERING_PIP: return "entering-pip";
+            case ENTERED_PIP: return "entered-pip";
+            case CHANGING_PIP_BOUNDS: return "changing-bounds";
+            case CHANGED_PIP_BOUNDS: return "changed-bounds";
+            case EXITING_PIP: return "exiting-pip";
+            case EXITED_PIP: return "exited-pip";
+        }
+        throw new IllegalStateException("Unknown state: " + mState);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("PipTransitionState(mState=%s, mInSwipePipToHomeTransition=%b)",
+                stateToString(), mInSwipePipToHomeTransition);
+    }
+}
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 2a50b19..5e9451a 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
@@ -201,7 +201,7 @@
     private final DisplayImeController mDisplayImeController;
     private final DisplayInsetsController mDisplayInsetsController;
     private final TransactionPool mTransactionPool;
-    private final SplitScreenTransitions mSplitTransitions;
+    private SplitScreenTransitions mSplitTransitions;
     private final SplitscreenEventLogger mLogger;
     private final ShellExecutor mMainExecutor;
     // Cache live tile tasks while entering recents, evict them from stages in finish transaction
@@ -399,6 +399,11 @@
         return mSplitTransitions;
     }
 
+    @VisibleForTesting
+    void setSplitTransitions(SplitScreenTransitions splitScreenTransitions) {
+        mSplitTransitions = splitScreenTransitions;
+    }
+
     public boolean isSplitScreenVisible() {
         return mSideStageListener.mVisible && mMainStageListener.mVisible;
     }
@@ -583,7 +588,7 @@
         options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
         wct.startTask(taskId, options);
         // If this should be mixed, send the task to avoid split handle transition directly.
-        if (mMixedHandler != null && mMixedHandler.shouldSplitEnterMixed(taskId, mTaskOrganizer)) {
+        if (mMixedHandler != null && mMixedHandler.isTaskInPip(taskId, mTaskOrganizer)) {
             mTaskOrganizer.applyTransaction(wct);
             return;
         }
@@ -622,7 +627,7 @@
         wct.sendPendingIntent(intent, fillInIntent, options);
 
         // If this should be mixed, just send the intent to avoid split handle transition directly.
-        if (mMixedHandler != null && mMixedHandler.shouldSplitEnterMixed(intent)) {
+        if (mMixedHandler != null && mMixedHandler.isIntentInPip(intent)) {
             mTaskOrganizer.applyTransaction(wct);
             return;
         }
@@ -711,16 +716,7 @@
                 taskId1, taskId2, splitPosition, snapPosition);
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (taskId2 == INVALID_TASK_ID) {
-            if (mMainStage.containsTask(taskId1) || mSideStage.containsTask(taskId1)) {
-                prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
-            }
-            if (mRecentTasks.isPresent()) {
-                mRecentTasks.get().removeSplitPair(taskId1);
-            }
-            options1 = options1 != null ? options1 : new Bundle();
-            addActivityOptions(options1, null);
-            wct.startTask(taskId1, options1);
-            mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
+            startSingleTask(taskId1, options1, wct, remoteTransition);
             return;
         }
 
@@ -741,11 +737,15 @@
                 "startIntentAndTask: intent=%s task1=%d position=%d snapPosition=%d",
                 pendingIntent.getIntent(), taskId, splitPosition, snapPosition);
         final WindowContainerTransaction wct = new WindowContainerTransaction();
-        if (taskId == INVALID_TASK_ID) {
-            options1 = options1 != null ? options1 : new Bundle();
-            addActivityOptions(options1, null);
-            wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
-            mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
+        boolean firstIntentPipped = mMixedHandler.isIntentInPip(pendingIntent);
+        boolean secondTaskPipped = mMixedHandler.isTaskInPip(taskId, mTaskOrganizer);
+        if (taskId == INVALID_TASK_ID || secondTaskPipped) {
+            startSingleIntent(pendingIntent, fillInIntent, options1, wct, remoteTransition);
+            return;
+        }
+
+        if (firstIntentPipped) {
+            startSingleTask(taskId, options2, wct, remoteTransition);
             return;
         }
 
@@ -757,6 +757,24 @@
         startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
     }
 
+    /**
+     * @param taskId Starts this task in fullscreen, removing it from existing pairs if it was part
+     *               of one.
+     */
+    private void startSingleTask(int taskId, Bundle options, WindowContainerTransaction wct,
+            RemoteTransition remoteTransition) {
+        if (mMainStage.containsTask(taskId) || mSideStage.containsTask(taskId)) {
+            prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+        }
+        if (mRecentTasks.isPresent()) {
+            mRecentTasks.get().removeSplitPair(taskId);
+        }
+        options = options != null ? options : new Bundle();
+        addActivityOptions(options, null);
+        wct.startTask(taskId, options);
+        mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
+    }
+
     /** Starts a shortcut and a task to a split pair in one transition. */
     void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
             int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
@@ -844,6 +862,21 @@
             return;
         }
 
+        boolean handledForPipSplitLaunch = handlePippedSplitIntentsLaunch(
+                pendingIntent1,
+                pendingIntent2,
+                options1,
+                options2,
+                shortcutInfo1,
+                shortcutInfo2,
+                wct,
+                fillInIntent1,
+                fillInIntent2,
+                remoteTransition);
+        if (handledForPipSplitLaunch) {
+            return;
+        }
+
         if (!mMainStage.isActive()) {
             // Build a request WCT that will launch both apps such that task 0 is on the main stage
             // while task 1 is on the side stage.
@@ -878,6 +911,46 @@
         setEnterInstanceId(instanceId);
     }
 
+    /**
+     * Checks if either of the apps in the desired split launch is currently in Pip. If so, it will
+     * launch the non-pipped app as a fullscreen app, otherwise no-op.
+     */
+    private boolean handlePippedSplitIntentsLaunch(PendingIntent pendingIntent1,
+            PendingIntent pendingIntent2, Bundle options1, Bundle options2,
+            ShortcutInfo shortcutInfo1, ShortcutInfo shortcutInfo2, WindowContainerTransaction wct,
+            Intent fillInIntent1, Intent fillInIntent2, RemoteTransition remoteTransition) {
+        // If one of the split apps to start is in Pip, only launch the non-pip app in fullscreen
+        boolean firstIntentPipped = mMixedHandler.isIntentInPip(pendingIntent1);
+        boolean secondIntentPipped = mMixedHandler.isIntentInPip(pendingIntent2);
+        if (firstIntentPipped || secondIntentPipped) {
+            Bundle options = secondIntentPipped ? options1 : options2;
+            options = options == null ? new Bundle() : options;
+            addActivityOptions(options, null);
+            if (shortcutInfo1 != null || shortcutInfo2 != null) {
+                ShortcutInfo infoToLaunch = secondIntentPipped ? shortcutInfo1 : shortcutInfo2;
+                wct.startShortcut(mContext.getPackageName(), infoToLaunch, options);
+                mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
+            } else {
+                PendingIntent intentToLaunch = secondIntentPipped ? pendingIntent1 : pendingIntent2;
+                Intent fillInIntentToLaunch = secondIntentPipped ? fillInIntent1 : fillInIntent2;
+                startSingleIntent(intentToLaunch, fillInIntentToLaunch, options, wct,
+                        remoteTransition);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /** @param pendingIntent Starts this intent in fullscreen */
+    private void startSingleIntent(PendingIntent pendingIntent, Intent fillInIntent, Bundle options,
+            WindowContainerTransaction wct,
+            RemoteTransition remoteTransition) {
+        Bundle optionsToLaunch = options != null ? options : new Bundle();
+        addActivityOptions(optionsToLaunch, null);
+        wct.sendPendingIntent(pendingIntent, fillInIntent, optionsToLaunch);
+        mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
+    }
+
     /** Starts a pair of tasks using legacy transition. */
     void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1,
             int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 4bc0dc0..4d02ec2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -563,22 +563,23 @@
 
     /** Use to when split use intent to enter, check if this enter transition should be mixed or
      * not.*/
-    public boolean shouldSplitEnterMixed(PendingIntent intent) {
+    public boolean isIntentInPip(PendingIntent intent) {
         // Check if this intent package is same as pip one or not, if true we want let the pip
         // task enter split.
         if (mPipHandler != null) {
-            return mPipHandler.isInPipPackage(SplitScreenUtils.getPackageName(intent.getIntent()));
+            return mPipHandler
+                    .isPackageActiveInPip(SplitScreenUtils.getPackageName(intent.getIntent()));
         }
         return false;
     }
 
     /** Use to when split use taskId to enter, check if this enter transition should be mixed or
      * not.*/
-    public boolean shouldSplitEnterMixed(int taskId, ShellTaskOrganizer shellTaskOrganizer) {
+    public boolean isTaskInPip(int taskId, ShellTaskOrganizer shellTaskOrganizer) {
         // Check if this intent package is same as pip one or not, if true we want let the pip
         // task enter split.
         if (mPipHandler != null) {
-            return mPipHandler.isInPipPackage(
+            return mPipHandler.isPackageActiveInPip(
                     SplitScreenUtils.getPackageName(taskId, shellTaskOrganizer));
         }
         return false;
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
index 5b2ffec..4dd14f4 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
@@ -20,6 +20,8 @@
     <option name="isolated-storage" value="false"/>
 
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
         <!-- prevents the phone from restarting -->
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
index 9f7d9fc..5c86a38 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
@@ -20,6 +20,8 @@
     <option name="isolated-storage" value="false"/>
 
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
         <!-- prevents the phone from restarting -->
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
index 882b200..aa70c09 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
@@ -20,6 +20,8 @@
     <option name="isolated-storage" value="false"/>
 
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
         <!-- prevents the phone from restarting -->
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
index f5a8655..bf040d2 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
@@ -20,6 +20,8 @@
     <option name="isolated-storage" value="false"/>
 
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
         <!-- prevents the phone from restarting -->
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
index 51a55e35..c7c804f2 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
@@ -20,6 +20,8 @@
     <option name="isolated-storage" value="false"/>
 
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
         <!-- prevents the phone from restarting -->
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
index 05f937a..214bdfa 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
@@ -20,6 +20,8 @@
     <option name="isolated-storage" value="false"/>
 
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
         <!-- prevents the phone from restarting -->
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/PipTransitionStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/PipTransitionStateTest.java
new file mode 100644
index 0000000..bd8ac37
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/PipTransitionStateTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.wm.shell.pip2;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.testing.AndroidTestingRunner;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.pip2.phone.PipTransitionState;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit test against {@link PhoneSizeSpecSource}.
+ *
+ * This test mocks the PiP2 flag to be true.
+ */
+@RunWith(AndroidTestingRunner.class)
+public class PipTransitionStateTest extends ShellTestCase {
+    private static final String EXTRA_ENTRY_KEY = "extra_entry_key";
+    private PipTransitionState mPipTransitionState;
+    private PipTransitionState.PipTransitionStateChangedListener mStateChangedListener;
+    private Parcelable mEmptyParcelable;
+
+    @Before
+    public void setUp() {
+        mPipTransitionState = new PipTransitionState();
+        mPipTransitionState.setState(PipTransitionState.UNDEFINED);
+        mEmptyParcelable = new Bundle();
+    }
+
+    @Test
+    public void testEnteredState_withoutExtra() {
+        mStateChangedListener = (oldState, newState, extra) -> {
+            Assert.assertEquals(PipTransitionState.ENTERED_PIP, newState);
+            Assert.assertNull(extra);
+        };
+        mPipTransitionState.addPipTransitionStateChangedListener(mStateChangedListener);
+        mPipTransitionState.setState(PipTransitionState.ENTERED_PIP);
+        mPipTransitionState.removePipTransitionStateChangedListener(mStateChangedListener);
+    }
+
+    @Test
+    public void testEnteredState_withExtra() {
+        mStateChangedListener = (oldState, newState, extra) -> {
+            Assert.assertEquals(PipTransitionState.ENTERED_PIP, newState);
+            Assert.assertNotNull(extra);
+            Assert.assertEquals(mEmptyParcelable, extra.getParcelable(EXTRA_ENTRY_KEY));
+        };
+        Bundle extra = new Bundle();
+        extra.putParcelable(EXTRA_ENTRY_KEY, mEmptyParcelable);
+
+        mPipTransitionState.addPipTransitionStateChangedListener(mStateChangedListener);
+        mPipTransitionState.setState(PipTransitionState.ENTERED_PIP, extra);
+        mPipTransitionState.removePipTransitionStateChangedListener(mStateChangedListener);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testEnteringState_withoutExtra() {
+        mPipTransitionState.setState(PipTransitionState.ENTERING_PIP);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSwipingToPipState_withoutExtra() {
+        mPipTransitionState.setState(PipTransitionState.SWIPING_TO_PIP);
+    }
+
+    @Test
+    public void testCustomState_withExtra_thenEntered_withoutExtra() {
+        final int customState = mPipTransitionState.getCustomState();
+        mStateChangedListener = (oldState, newState, extra) -> {
+            if (newState == customState) {
+                Assert.assertNotNull(extra);
+                Assert.assertEquals(mEmptyParcelable, extra.getParcelable(EXTRA_ENTRY_KEY));
+                return;
+            } else if (newState == PipTransitionState.ENTERED_PIP) {
+                Assert.assertNull(extra);
+                return;
+            }
+            Assert.fail("Neither custom not ENTERED_PIP state is received.");
+        };
+        Bundle extra = new Bundle();
+        extra.putParcelable(EXTRA_ENTRY_KEY, mEmptyParcelable);
+
+        mPipTransitionState.addPipTransitionStateChangedListener(mStateChangedListener);
+        mPipTransitionState.setState(customState, extra);
+        mPipTransitionState.setState(PipTransitionState.ENTERED_PIP);
+        mPipTransitionState.removePipTransitionStateChangedListener(mStateChangedListener);
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index d819261..d7c3835 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -40,10 +40,12 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.PendingIntent;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -51,6 +53,7 @@
 import android.os.Looper;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.window.RemoteTransition;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
@@ -74,6 +77,7 @@
 import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.DefaultMixedHandler;
 import com.android.wm.shell.transition.HomeTransitionObserver;
 import com.android.wm.shell.transition.Transitions;
 
@@ -111,6 +115,8 @@
     private TransactionPool mTransactionPool;
     @Mock
     private LaunchAdjacentController mLaunchAdjacentController;
+    @Mock
+    private DefaultMixedHandler mDefaultMixedHandler;
 
     private final Rect mBounds1 = new Rect(10, 20, 30, 40);
     private final Rect mBounds2 = new Rect(5, 10, 15, 20);
@@ -370,6 +376,96 @@
         }
     }
 
+    @Test
+    public void testSplitIntentAndTaskWithPippedApp_launchFullscreen() {
+        int taskId = 9;
+        SplitScreenTransitions splitScreenTransitions =
+                spy(mStageCoordinator.getSplitTransitions());
+        mStageCoordinator.setSplitTransitions(splitScreenTransitions);
+        mStageCoordinator.setMixedHandler(mDefaultMixedHandler);
+        PendingIntent pendingIntent = mock(PendingIntent.class);
+        RemoteTransition remoteTransition = mock(RemoteTransition.class);
+        when(remoteTransition.getDebugName()).thenReturn("");
+        // Test launching second task full screen
+        when(mDefaultMixedHandler.isIntentInPip(pendingIntent)).thenReturn(true);
+        mStageCoordinator.startIntentAndTask(
+                pendingIntent,
+                null /*fillInIntent*/,
+                null /*option1*/,
+                taskId,
+                null /*option2*/,
+                0 /*splitPosition*/,
+                1 /*snapPosition*/,
+                remoteTransition /*remoteTransition*/,
+                null /*instanceId*/);
+        verify(splitScreenTransitions, times(1))
+                .startFullscreenTransition(any(), any());
+
+        // Test launching first intent fullscreen
+        when(mDefaultMixedHandler.isIntentInPip(pendingIntent)).thenReturn(false);
+        when(mDefaultMixedHandler.isTaskInPip(taskId, mTaskOrganizer)).thenReturn(true);
+        mStageCoordinator.startIntentAndTask(
+                pendingIntent,
+                null /*fillInIntent*/,
+                null /*option1*/,
+                taskId,
+                null /*option2*/,
+                0 /*splitPosition*/,
+                1 /*snapPosition*/,
+                remoteTransition /*remoteTransition*/,
+                null /*instanceId*/);
+        verify(splitScreenTransitions, times(2))
+                .startFullscreenTransition(any(), any());
+    }
+
+    @Test
+    public void testSplitIntentsWithPippedApp_launchFullscreen() {
+        SplitScreenTransitions splitScreenTransitions =
+                spy(mStageCoordinator.getSplitTransitions());
+        mStageCoordinator.setSplitTransitions(splitScreenTransitions);
+        mStageCoordinator.setMixedHandler(mDefaultMixedHandler);
+        PendingIntent pendingIntent = mock(PendingIntent.class);
+        PendingIntent pendingIntent2 = mock(PendingIntent.class);
+        RemoteTransition remoteTransition = mock(RemoteTransition.class);
+        when(remoteTransition.getDebugName()).thenReturn("");
+        // Test launching second task full screen
+        when(mDefaultMixedHandler.isIntentInPip(pendingIntent)).thenReturn(true);
+        mStageCoordinator.startIntents(
+                pendingIntent,
+                null /*fillInIntent*/,
+                null /*shortcutInfo1*/,
+                new Bundle(),
+                pendingIntent2,
+                null /*fillInIntent2*/,
+                null /*shortcutInfo1*/,
+                new Bundle(),
+                0 /*splitPosition*/,
+                1 /*snapPosition*/,
+                remoteTransition /*remoteTransition*/,
+                null /*instanceId*/);
+        verify(splitScreenTransitions, times(1))
+                .startFullscreenTransition(any(), any());
+
+        // Test launching first intent fullscreen
+        when(mDefaultMixedHandler.isIntentInPip(pendingIntent)).thenReturn(false);
+        when(mDefaultMixedHandler.isIntentInPip(pendingIntent2)).thenReturn(true);
+        mStageCoordinator.startIntents(
+                pendingIntent,
+                null /*fillInIntent*/,
+                null /*shortcutInfo1*/,
+                new Bundle(),
+                pendingIntent2,
+                null /*fillInIntent2*/,
+                null /*shortcutInfo1*/,
+                new Bundle(),
+                0 /*splitPosition*/,
+                1 /*snapPosition*/,
+                remoteTransition /*remoteTransition*/,
+                null /*instanceId*/);
+        verify(splitScreenTransitions, times(2))
+                .startFullscreenTransition(any(), any());
+    }
+
     private Transitions createTestTransitions() {
         ShellInit shellInit = new ShellInit(mMainExecutor);
         final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h
index 1fcb692..cfca480 100644
--- a/libs/hwui/hwui/DrawTextFunctor.h
+++ b/libs/hwui/hwui/DrawTextFunctor.h
@@ -34,7 +34,9 @@
 
 namespace android {
 
-inline constexpr int kHighContrastTextBorderWidth = 4;
+// These should match the constants in framework/base/core/java/android/text/Layout.java
+inline constexpr float kHighContrastTextBorderWidth = 4.0f;
+inline constexpr float kHighContrastTextBorderWidthFactor = 0.2f;
 
 static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness,
                               const Paint& paint, Canvas* canvas) {
@@ -48,7 +50,16 @@
     paint->setShader(nullptr);
     paint->setColorFilter(nullptr);
     paint->setLooper(nullptr);
-    paint->setStrokeWidth(kHighContrastTextBorderWidth + 0.04 * paint->getSkFont().getSize());
+
+    if (flags::high_contrast_text_small_text_rect()) {
+        paint->setStrokeWidth(
+                std::max(kHighContrastTextBorderWidth,
+                         kHighContrastTextBorderWidthFactor * paint->getSkFont().getSize()));
+    } else {
+        auto borderWidthFactor = 0.04f;
+        paint->setStrokeWidth(kHighContrastTextBorderWidth +
+                              borderWidthFactor * paint->getSkFont().getSize());
+    }
     paint->setStrokeJoin(SkPaint::kRound_Join);
     paint->setLooper(nullptr);
 }
@@ -106,36 +117,7 @@
             Paint outlinePaint(paint);
             simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
             outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
-            if (flags::high_contrast_text_small_text_rect()) {
-                const SkFont& font = paint.getSkFont();
-                auto padding = kHighContrastTextBorderWidth + 0.1f * font.getSize();
-
-                // Draw the background only behind each glyph's bounds. We do this instead of using
-                // the bounds of the entire layout, because the layout includes alignment whitespace
-                // etc which can obscure other text from separate passes (e.g. emojis).
-                // Merge all the glyph bounds into one rect for this line, since drawing a rect for
-                // each glyph is expensive.
-                SkRect glyphBounds;
-                SkRect bgBounds;
-                for (size_t i = start; i < end; i++) {
-                    auto glyph = layout.getGlyphId(i);
-
-                    font.getBounds(reinterpret_cast<const SkGlyphID*>(&glyph), 1, &glyphBounds,
-                                   &paint);
-                    glyphBounds.offset(layout.getX(i), layout.getY(i));
-
-                    bgBounds.join(glyphBounds);
-                }
-
-                if (!bgBounds.isEmpty()) {
-                    bgBounds.offset(x, y);
-                    bgBounds.outset(padding, padding);
-                    canvas->drawRect(bgBounds.fLeft, bgBounds.fTop, bgBounds.fRight,
-                                     bgBounds.fBottom, outlinePaint);
-                }
-            } else {
-                canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
-            }
+            canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
 
             // inner
             gDrawTextBlobMode = DrawTextBlobMode::HctInner;
diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml
index 45253bb..68b81db 100644
--- a/packages/SettingsLib/res/values/config.xml
+++ b/packages/SettingsLib/res/values/config.xml
@@ -22,6 +22,9 @@
     <!-- Threshold in micro watts above which a charger is rated as "fast"; 1.5A @ 5V  -->
     <integer name="config_chargingFastThreshold">7500000</integer>
 
+    <!-- Threshold in micro watts above which a charger is rated as "fast"; 20W  -->
+    <integer name="config_chargingFastThreshold_v2">20000000</integer>
+
     <!-- When true, show 1/2G networks as 3G. -->
     <bool name="config_showMin3G">false</bool>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
index 2032328..f659e38 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
@@ -145,7 +145,8 @@
         final int slowThreshold = context.getResources().getInteger(
                 R.integer.config_chargingSlowlyThreshold);
         final int fastThreshold = context.getResources().getInteger(
-                R.integer.config_chargingFastThreshold);
+                getFastChargingThresholdResId());
+
         return maxChargingWattage <= 0 ? CHARGING_UNKNOWN :
                 maxChargingWattage < slowThreshold ? CHARGING_SLOWLY :
                         maxChargingWattage > fastThreshold ? CHARGING_FAST :
@@ -382,7 +383,7 @@
                 < context.getResources().getInteger(R.integer.config_chargingSlowlyThreshold)) {
             return CHARGING_SLOWLY;
         } else if (maxChargingMicroWatt
-                > context.getResources().getInteger(R.integer.config_chargingFastThreshold)) {
+                > context.getResources().getInteger(getFastChargingThresholdResId())) {
             return CHARGING_FAST;
         } else {
             return CHARGING_REGULAR;
@@ -410,4 +411,10 @@
             return -1;
         }
     }
+
+    private static int getFastChargingThresholdResId() {
+        return BatteryUtils.isChargingStringV2Enabled()
+                        ? R.integer.config_chargingFastThreshold_v2
+                        : R.integer.config_chargingFastThreshold;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
index 327e470..ca3af53 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
@@ -27,6 +27,7 @@
 import android.util.ArraySet;
 import android.view.accessibility.AccessibilityManager;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import java.util.List;
@@ -97,9 +98,18 @@
     /** Used to override the system property to enable or reset for charging string V2. */
     @VisibleForTesting
     public static void setChargingStringV2Enabled(Boolean enabled) {
-        SystemProperties.set(
-                BatteryUtils.PROPERTY_CHARGING_STRING_V2_KEY,
-                enabled == null ? "" : String.valueOf(enabled));
+        setChargingStringV2Enabled(enabled, true /* updateProperty */);
+    }
+
+    /** Used to override the system property to enable or reset for charging string V2. */
+    @VisibleForTesting
+    public static void setChargingStringV2Enabled(
+            @Nullable Boolean enabled, boolean updateProperty) {
+        if (updateProperty) {
+            SystemProperties.set(
+                    BatteryUtils.PROPERTY_CHARGING_STRING_V2_KEY,
+                    enabled == null ? "" : String.valueOf(enabled));
+        }
         BatteryUtils.sChargingStringV2Enabled = enabled;
     }
 }
diff --git a/packages/SettingsLib/tests/unit/src/com/android/settingslib/fuelgague/BatteryStatusTest.kt b/packages/SettingsLib/tests/unit/src/com/android/settingslib/fuelgague/BatteryStatusTest.kt
index 6c0c1a7..4940610 100644
--- a/packages/SettingsLib/tests/unit/src/com/android/settingslib/fuelgague/BatteryStatusTest.kt
+++ b/packages/SettingsLib/tests/unit/src/com/android/settingslib/fuelgague/BatteryStatusTest.kt
@@ -38,6 +38,7 @@
 import com.android.settingslib.fuelgauge.BatteryStatus.CHARGING_SLOWLY
 import com.android.settingslib.fuelgauge.BatteryStatus.CHARGING_UNKNOWN
 import com.android.settingslib.fuelgauge.BatteryStatus.isBatteryDefender
+import com.android.settingslib.fuelgauge.BatteryUtils
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import java.util.Optional
@@ -253,12 +254,17 @@
         private val maxChargingCurrent: Optional<Int>,
         private val maxChargingVoltage: Optional<Int>,
         private val expectedChargingSpeed: Int,
+        private val chargingStringV2Enabled: Boolean,
     ) {
 
         val context: Context = ApplicationProvider.getApplicationContext()
 
         @Test
         fun getChargingSpeed_() {
+            BatteryUtils.setChargingStringV2Enabled(
+                chargingStringV2Enabled,
+                false /* updateProperty */
+            )
             val batteryChangedIntent =
                 Intent(Intent.ACTION_BATTERY_CHANGED).apply {
                     maxChargingCurrent.ifPresent { putExtra(EXTRA_MAX_CHARGING_CURRENT, it) }
@@ -278,37 +284,57 @@
                         "maxCurrent=n/a, maxVoltage=n/a -> UNKNOWN",
                         Optional.empty<Int>(),
                         Optional.empty<Int>(),
-                        CHARGING_UNKNOWN
+                        CHARGING_UNKNOWN,
+                        false /* chargingStringV2Enabled */
                     ),
                     arrayOf(
                         "maxCurrent=0, maxVoltage=9000000 -> UNKNOWN",
                         Optional.of(0),
                         Optional.of(0),
-                        CHARGING_UNKNOWN
+                        CHARGING_UNKNOWN,
+                        false /* chargingStringV2Enabled */
                     ),
                     arrayOf(
                         "maxCurrent=1500000, maxVoltage=5000000 -> CHARGING_REGULAR",
                         Optional.of(1500000),
                         Optional.of(5000000),
-                        CHARGING_REGULAR
+                        CHARGING_REGULAR,
+                        false /* chargingStringV2Enabled */
                     ),
                     arrayOf(
                         "maxCurrent=1000000, maxVoltage=5000000 -> CHARGING_REGULAR",
                         Optional.of(1000000),
                         Optional.of(5000000),
-                        CHARGING_REGULAR
+                        CHARGING_REGULAR,
+                        false /* chargingStringV2Enabled */
                     ),
                     arrayOf(
                         "maxCurrent=1500001, maxVoltage=5000000 -> CHARGING_FAST",
                         Optional.of(1501000),
                         Optional.of(5000000),
-                        CHARGING_FAST
+                        CHARGING_FAST,
+                        false /* chargingStringV2Enabled */
                     ),
                     arrayOf(
                         "maxCurrent=999999, maxVoltage=5000000 -> CHARGING_SLOWLY",
                         Optional.of(999999),
                         Optional.of(5000000),
-                        CHARGING_SLOWLY
+                        CHARGING_SLOWLY,
+                        false /* chargingStringV2Enabled */
+                    ),
+                    arrayOf(
+                        "maxCurrent=3000000, maxVoltage=9000000 -> CHARGING_FAST",
+                        Optional.of(3000000),
+                        Optional.of(9000000),
+                        CHARGING_FAST,
+                        true /* chargingStringV2Enabled */
+                    ),
+                    arrayOf(
+                        "maxCurrent=2200000, maxVoltage=9000000 -> CHARGING_REGULAR",
+                        Optional.of(2200000),
+                        Optional.of(9000000),
+                        CHARGING_REGULAR,
+                        true /* chargingStringV2Enabled */
                     ),
                 )
         }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index e07cd05..ec3c003 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -27,6 +27,7 @@
 import com.android.compose.animation.scene.transitions
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
+import com.android.systemui.communal.ui.compose.extensions.allowGestures
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.communal.util.CommunalColors
 import com.android.systemui.res.R
@@ -79,6 +80,7 @@
 ) {
     val coroutineScope = rememberCoroutineScope()
     val currentSceneKey: SceneKey by viewModel.currentScene.collectAsState(CommunalScenes.Blank)
+    val touchesAllowed by viewModel.touchesAllowed.collectAsState(initial = false)
     val state: MutableSceneTransitionLayoutState = remember {
         MutableSceneTransitionLayoutState(
             initialScene = currentSceneKey,
@@ -128,6 +130,10 @@
             CommunalScene(viewModel, colors, dialogFactory, modifier = modifier)
         }
     }
+
+    // Touches on the notification shade in blank areas fall through to the glanceable hub. When the
+    // shade is showing, we block all touches in order to prevent this unwanted behavior.
+    Box(modifier = Modifier.fillMaxSize().allowGestures(touchesAllowed))
 }
 
 /** Scene containing the glanceable hub UI. */
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 7d56a67..dff9b3b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -105,6 +105,7 @@
 import androidx.compose.ui.semantics.CustomAccessibilityAction
 import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.semantics.customActions
+import androidx.compose.ui.semantics.onClick
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.testTagsAsResourceId
 import androidx.compose.ui.text.style.TextAlign
@@ -841,19 +842,31 @@
     widgetConfigurator: WidgetConfigurator?,
     modifier: Modifier = Modifier,
 ) {
+    val context = LocalContext.current
     val isFocusable by viewModel.isFocusable.collectAsState(initial = false)
-
+    val accessibilityLabel =
+        remember(model, context) {
+            model.providerInfo.loadLabel(context.packageManager).toString().trim()
+        }
+    val clickActionLabel = stringResource(R.string.accessibility_action_label_select_widget)
     Box(
         modifier =
-            modifier.thenIf(!viewModel.isEditMode && model.inQuietMode) {
-                Modifier.pointerInput(Unit) {
-                    // consume tap to prevent the child view from triggering interactions with the
-                    // app widget
-                    observeTaps(shouldConsume = true) { _ ->
-                        viewModel.onOpenEnableWorkProfileDialog()
+            modifier
+                .thenIf(!viewModel.isEditMode && model.inQuietMode) {
+                    Modifier.pointerInput(Unit) {
+                        // consume tap to prevent the child view from triggering interactions with
+                        // the app widget
+                        observeTaps(shouldConsume = true) { _ ->
+                            viewModel.onOpenEnableWorkProfileDialog()
+                        }
                     }
                 }
-            }
+                .thenIf(viewModel.isEditMode) {
+                    Modifier.semantics {
+                        contentDescription = accessibilityLabel
+                        onClick(label = clickActionLabel, action = null)
+                    }
+                }
     ) {
         AndroidView(
             modifier = Modifier.fillMaxSize().allowGestures(allowed = !viewModel.isEditMode),
@@ -865,6 +878,7 @@
                         // Remove the extra padding applied to AppWidgetHostView to allow widgets to
                         // occupy the entire box.
                         setPadding(0)
+                        accessibilityDelegate = viewModel.widgetAccessibilityDelegate
                     }
             },
             update = {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index e7bfee3..33d2cc4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -40,6 +40,7 @@
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.input.pointer.pointerInteropFilter
+import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.toOffset
 import androidx.compose.ui.unit.toSize
@@ -274,6 +275,9 @@
         AnimatedVisibility(
             modifier =
                 Modifier.matchParentSize()
+                    // Avoid taking focus away from the content when using explore-by-touch with
+                    // accessibility tools.
+                    .clearAndSetSemantics {}
                     // Do not consume motion events in the highlighted item and pass them down to
                     // the content.
                     .pointerInteropFilter { false },
diff --git a/packages/SystemUI/docs/scene.md b/packages/SystemUI/docs/scene.md
index fb8a271..2f50bbd 100644
--- a/packages/SystemUI/docs/scene.md
+++ b/packages/SystemUI/docs/scene.md
@@ -62,7 +62,7 @@
 
 NOTE: in case these instructions become stale and don't actually enable the
 framework, please make sure `SceneContainerFlag.isEnabled` in the
-[`SceneContainerFlags.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt)
+[`SceneContainerFlag.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt)
 file evalutes to `true`.
 
 1.  Set a collection of **aconfig flags** to `true` by running the following
@@ -74,10 +74,9 @@
     $ adb shell device_config override systemui com.android.systemui.keyguard_wm_state_refactor true
     $ adb shell device_config override systemui com.android.systemui.media_in_scene_container true
     $ adb shell device_config override systemui com.android.systemui.migrate_clocks_to_blueprint true
-    $ adb shell device_config override systemui com.android.systemui.notification_heads_up_refactor true
+    $ adb shell device_config override systemui com.android.systemui.notifications_heads_up_refactor true
     $ adb shell device_config override systemui com.android.systemui.predictive_back_sysui true
     $ adb shell device_config override systemui com.android.systemui.device_entry_udfps_refactor true
-    $ adb shell device_config override systemui com.android.systemui.refactor_keyguard_dismiss_intent true
     ```
 2.  **Restart** System UI by issuing the following command:
     ```console
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 9e9a002..569116c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -140,6 +140,7 @@
         underTest =
             CommunalViewModel(
                 testScope,
+                context.resources,
                 kosmos.keyguardTransitionInteractor,
                 kosmos.communalInteractor,
                 kosmos.communalTutorialInteractor,
@@ -343,6 +344,30 @@
         }
 
     @Test
+    fun touchesAllowed_shadeNotExpanded() =
+        testScope.runTest {
+            val touchesAllowed by collectLastValue(underTest.touchesAllowed)
+
+            // On keyguard without any shade expansion.
+            kosmos.fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+            shadeTestUtil.setLockscreenShadeExpansion(0f)
+            runCurrent()
+            assertThat(touchesAllowed).isTrue()
+        }
+
+    @Test
+    fun touchesAllowed_shadeExpanded() =
+        testScope.runTest {
+            val touchesAllowed by collectLastValue(underTest.touchesAllowed)
+
+            // On keyguard with shade fully expanded.
+            kosmos.fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+            shadeTestUtil.setLockscreenShadeExpansion(1f)
+            runCurrent()
+            assertThat(touchesAllowed).isFalse()
+        }
+
+    @Test
     fun isFocusable_isFalse_whenTransitioningAwayFromGlanceableHub() =
         testScope.runTest {
             val isFocusable by collectLastValue(underTest.isFocusable)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
index d702330..360f284 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
@@ -33,6 +33,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.os.PowerManager
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -46,6 +47,7 @@
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 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.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -317,6 +319,51 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetectedInAod_fromGone() =
+        testScope.runTest {
+            powerInteractor.setAwakeForTest()
+            transitionRepository.sendTransitionSteps(
+                from = KeyguardState.AOD,
+                to = KeyguardState.GONE,
+                testScope,
+            )
+            runCurrent()
+
+            // Make sure we're GONE.
+            assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+
+            // Start going to AOD on first button push
+            powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
+            transitionRepository.sendTransitionSteps(
+                listOf(
+                    TransitionStep(
+                        from = KeyguardState.GONE,
+                        to = KeyguardState.AOD,
+                        transitionState = TransitionState.STARTED,
+                        value = 0f
+                    ),
+                    TransitionStep(
+                        from = KeyguardState.GONE,
+                        to = KeyguardState.AOD,
+                        transitionState = TransitionState.RUNNING,
+                        value = 0.1f
+                    ),
+                ),
+                testScope = testScope,
+            )
+
+            // Detect a power gesture and then wake up.
+            reset(transitionRepository)
+            powerInteractor.onCameraLaunchGestureDetected()
+            powerInteractor.setAwakeForTest()
+            advanceTimeBy(100) // account for debouncing
+
+            assertThat(transitionRepository)
+                .startedTransition(from = KeyguardState.AOD, to = KeyguardState.GONE)
+        }
+
+    @Test
     fun testTransitionToLockscreen_onWakeUpFromAod_dismissibleKeyguard_securitySwipe() =
         testScope.runTest {
             kosmos.fakeKeyguardRepository.setKeyguardShowing(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
similarity index 85%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index 0a29821..3b6f6a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -12,20 +12,19 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
  */
 
 package com.android.systemui.keyguard.domain.interactor
 
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
-import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -33,7 +32,10 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.PowerInteractorFactory
-import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.domain.interactor.shadeLockscreenInteractor
+import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.argumentCaptor
@@ -50,15 +52,18 @@
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @ExperimentalCoroutinesApi
 @SmallTest
-@RunWith(AndroidJUnit4::class)
-class UdfpsKeyguardInteractorTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class UdfpsKeyguardInteractorTest(flags: FlagsParameterization?) : SysuiTestCase() {
     val kosmos = testKosmos()
     val testScope = kosmos.testScope
-    val configRepository = kosmos.fakeConfigurationRepository
     val keyguardRepository = kosmos.fakeKeyguardRepository
+    val shadeRepository = kosmos.fakeShadeRepository
+    val shadeTestUtil by lazy { kosmos.shadeTestUtil }
 
     private val burnInProgress = 1f
     private val burnInYOffset = 20
@@ -67,7 +72,6 @@
     private lateinit var bouncerRepository: KeyguardBouncerRepository
     private lateinit var fakeCommandQueue: FakeCommandQueue
     private lateinit var burnInInteractor: BurnInInteractor
-    private lateinit var shadeRepository: FakeShadeRepository
     private lateinit var powerInteractor: PowerInteractor
 
     @Mock private lateinit var burnInHelper: BurnInHelperWrapper
@@ -75,11 +79,22 @@
 
     private lateinit var underTest: UdfpsKeyguardInteractor
 
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags!!)
+    }
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         bouncerRepository = FakeKeyguardBouncerRepository()
-        shadeRepository = FakeShadeRepository()
         fakeCommandQueue = FakeCommandQueue()
         burnInInteractor =
             BurnInInteractor(
@@ -93,10 +108,10 @@
 
         underTest =
             UdfpsKeyguardInteractor(
-                configRepository,
                 burnInInteractor,
                 kosmos.keyguardInteractor,
-                shadeRepository,
+                kosmos.shadeInteractor,
+                kosmos.shadeLockscreenInteractor,
                 dialogManager,
             )
     }
@@ -183,13 +198,13 @@
             val qsProgress by collectLastValue(underTest.qsProgress)
             assertThat(qsProgress).isEqualTo(0f)
 
-            shadeRepository.setQsExpansion(.22f)
+            shadeTestUtil.setQsExpansion(.22f)
             assertThat(qsProgress).isEqualTo(.44f)
 
-            shadeRepository.setQsExpansion(.5f)
+            shadeTestUtil.setQsExpansion(.5f)
             assertThat(qsProgress).isEqualTo(1f)
 
-            shadeRepository.setQsExpansion(.7f)
+            shadeTestUtil.setQsExpansion(.7f)
             assertThat(qsProgress).isEqualTo(1f)
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index e3eca67..030aa24 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -19,24 +19,25 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.platform.test.flag.junit.FlagsParameterization
 import android.view.View
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
-import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.communalRepository
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.flags.parameterizeSceneContainerFlag
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 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.kosmos.testScope
-import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
 import com.android.systemui.statusbar.phone.dozeParameters
 import com.android.systemui.statusbar.phone.screenOffAnimationController
@@ -53,32 +54,50 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import com.android.systemui.Flags as AConfigFlags
 
 @SmallTest
-@RunWith(AndroidJUnit4::class)
-class KeyguardRootViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class KeyguardRootViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
-    private val keyguardRepository = kosmos.fakeKeyguardRepository
-    private val communalRepository = kosmos.communalRepository
-    private val screenOffAnimationController = kosmos.screenOffAnimationController
-    private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository
-    private val notificationsKeyguardInteractor = kosmos.notificationsKeyguardInteractor
-    private val dozeParameters = kosmos.dozeParameters
-    private val shadeRepository = kosmos.fakeShadeRepository
+    private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+    private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
+    private val communalRepository by lazy { kosmos.communalRepository }
+    private val screenOffAnimationController by lazy { kosmos.screenOffAnimationController }
+    private val deviceEntryRepository by lazy { kosmos.fakeDeviceEntryRepository }
+    private val notificationsKeyguardInteractor by lazy { kosmos.notificationsKeyguardInteractor }
+    private val dozeParameters by lazy { kosmos.dozeParameters }
+    private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
     private val underTest by lazy { kosmos.keyguardRootViewModel }
 
     private val viewState = ViewStateAccessor()
 
+    // add to init block
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return parameterizeSceneContainerFlag()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags!!)
+    }
+
     @Before
     fun setUp() {
-        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
         mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION)
-        mSetFlagsRule.disableFlags(
-            AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
-            AConfigFlags.FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT,
-        )
+        if (!SceneContainerFlag.isEnabled) {
+            mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
+            mSetFlagsRule.disableFlags(
+                AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
+                AConfigFlags.FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT,
+            )
+        }
     }
 
     @Test
@@ -336,10 +355,10 @@
                 testScope,
             )
 
-            shadeRepository.setQsExpansion(0f)
+            shadeTestUtil.setQsExpansion(0f)
             assertThat(alpha).isEqualTo(1f)
 
-            shadeRepository.setQsExpansion(0.5f)
+            shadeTestUtil.setQsExpansion(0.5f)
             assertThat(alpha).isEqualTo(0f)
         }
 
@@ -356,11 +375,11 @@
             )
 
             // Open the shade.
-            shadeRepository.setQsExpansion(1f)
+            shadeTestUtil.setQsExpansion(1f)
             assertThat(alpha).isEqualTo(0f)
 
             // Close the shade, alpha returns to 1.
-            shadeRepository.setQsExpansion(0f)
+            shadeTestUtil.setQsExpansion(0f)
             assertThat(alpha).isEqualTo(1f)
         }
 
@@ -377,11 +396,11 @@
             )
 
             // Open the shade.
-            shadeRepository.setQsExpansion(1f)
+            shadeTestUtil.setQsExpansion(1f)
             assertThat(alpha).isEqualTo(0f)
 
             // Close the shade, alpha is still 0 since we're not on the lockscreen.
-            shadeRepository.setQsExpansion(0f)
+            shadeTestUtil.setQsExpansion(0f)
             assertThat(alpha).isEqualTo(0f)
         }
 
@@ -400,7 +419,7 @@
             assertThat(alpha).isEqualTo(0f)
 
             // Try pulling down shade and ensure the value doesn't change
-            shadeRepository.setQsExpansion(0.5f)
+            shadeTestUtil.setQsExpansion(0.5f)
             assertThat(alpha).isEqualTo(0f)
         }
 
@@ -419,7 +438,7 @@
             assertThat(alpha).isEqualTo(0f)
 
             // Try pulling down shade and ensure the value doesn't change
-            shadeRepository.setQsExpansion(0.5f)
+            shadeTestUtil.setQsExpansion(0.5f)
             assertThat(alpha).isEqualTo(0f)
         }
 
@@ -438,7 +457,7 @@
             assertThat(alpha).isEqualTo(0f)
 
             // Try pulling down shade and ensure the value doesn't change
-            shadeRepository.setQsExpansion(0.5f)
+            shadeTestUtil.setQsExpansion(0.5f)
             assertThat(alpha).isEqualTo(0f)
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index 4907359..ec2cb04 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -16,13 +16,15 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.authController
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.flags.Flags
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
 import com.android.systemui.keyguard.shared.model.ClockSize
@@ -40,15 +42,29 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(AndroidJUnit4::class)
-class LockscreenContentViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class LockscreenContentViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
 
     private val kosmos: Kosmos = testKosmos()
 
     lateinit var underTest: LockscreenContentViewModel
 
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags!!)
+    }
+
     @Before
     fun setup() {
         with(kosmos) {
@@ -77,6 +93,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun clockSize_withLargeClock_true() =
         with(kosmos) {
             testScope.runTest {
@@ -87,6 +104,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun clockSize_withSmallClock_false() =
         with(kosmos) {
             testScope.runTest {
@@ -109,6 +127,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun areNotificationsVisible_withSmallClock_true() =
         with(kosmos) {
             testScope.runTest {
@@ -119,6 +138,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun areNotificationsVisible_withLargeClock_false() =
         with(kosmos) {
             testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 776f1a5..bc9d257 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.data.repository.fakePowerRepository
 import com.android.systemui.power.shared.model.WakefulnessState
@@ -57,6 +58,7 @@
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
+@EnableSceneContainer
 class LockscreenSceneViewModelTest : SysuiTestCase() {
 
     companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
index 769a54a..13d6411 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
@@ -58,13 +58,16 @@
 import com.android.systemui.util.CarrierConfigTracker
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Assume.assumeFalse
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
[email protected]
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class InternetTileDataInteractorTest : SysuiTestCase() {
@@ -141,6 +144,7 @@
         underTest =
             InternetTileDataInteractor(
                 context,
+                testScope.coroutineContext,
                 testScope.backgroundScope,
                 airplaneModeRepository,
                 connectivityRepository,
@@ -433,8 +437,44 @@
                 .isEqualTo(expectedCd)
         }
 
+    /**
+     * We expect a RuntimeException because [underTest] instantiates a SignalDrawable on the
+     * provided context, and so the SignalDrawable constructor attempts to instantiate a Handler()
+     * on the mentioned context. Since that context does not have a looper assigned to it, the
+     * handler instantiation will throw a RuntimeException.
+     *
+     * TODO(b/338068066): Robolectric behavior differs in that it does not throw the exception
+     * So either we should make Robolectric behvase similar to the device test, or change this
+     * test to look for a different signal than the exception, when run by Robolectric. For now
+     * we just assume the test is not Robolectric.
+     */
+    @Test(expected = java.lang.RuntimeException::class)
+    fun mobileDefault_usesNetworkNameAndIcon_throwsRunTimeException() =
+        testScope.runTest {
+            assumeFalse(isRobolectricTest());
+
+            collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
+
+            connectivityRepository.setMobileConnected()
+            mobileConnectionsRepository.mobileIsDefault.value = true
+            mobileConnectionRepository.apply {
+                setAllLevels(3)
+                setAllRoaming(false)
+                networkName.value = NetworkNameModel.Default("test network")
+            }
+
+            runCurrent()
+        }
+
+    /**
+     * See [mobileDefault_usesNetworkNameAndIcon_throwsRunTimeException] for description of the
+     * problem this test solves. The solution here is to assign a looper to the context via
+     * RunWithLooper. In the production code, the solution is to use a Main CoroutineContext for
+     * creating the SignalDrawable.
+     */
+    @TestableLooper.RunWithLooper
     @Test
-    fun mobileDefault_usesNetworkNameAndIcon() =
+    fun mobileDefault_run_withLooper_usesNetworkNameAndIcon() =
         testScope.runTest {
             val latest by
                 collectLastValue(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
index 818c19c..4e06855 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
@@ -16,22 +16,26 @@
 
 package com.android.systemui.volume.panel.ui.viewmodel
 
+import android.content.Intent
+import android.content.applicationContext
 import android.content.res.Configuration
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.policy.fakeConfigurationController
 import com.android.systemui.testKosmos
 import com.android.systemui.volume.panel.componentByKey
 import com.android.systemui.volume.panel.componentsLayoutManager
 import com.android.systemui.volume.panel.criteriaByKey
-import com.android.systemui.volume.panel.dagger.factory.KosmosVolumePanelComponentFactory
 import com.android.systemui.volume.panel.mockVolumePanelUiComponentProvider
 import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
 import com.android.systemui.volume.panel.ui.layout.DefaultComponentsLayoutManager
 import com.android.systemui.volume.panel.unavailableCriteria
+import com.android.systemui.volume.panel.volumePanelViewModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
@@ -51,67 +55,49 @@
 
     private lateinit var underTest: VolumePanelViewModel
 
-    private fun initUnderTest() {
-        underTest =
-            VolumePanelViewModel(
-                testableResources.resources,
-                kosmos.testScope.backgroundScope,
-                KosmosVolumePanelComponentFactory(kosmos),
-                kosmos.fakeConfigurationController,
+    @Test
+    fun dismissingPanel_changesVisibility() = test {
+        testScope.runTest {
+            assertThat(underTest.volumePanelState.value.isVisible).isTrue()
+
+            underTest.dismissPanel()
+            runCurrent()
+
+            assertThat(underTest.volumePanelState.value.isVisible).isFalse()
+        }
+    }
+
+    @Test
+    fun orientationChanges_panelOrientationChanges() = test {
+        testScope.runTest {
+            val volumePanelState by collectLastValue(underTest.volumePanelState)
+            testableResources.overrideConfiguration(
+                Configuration().apply { orientation = Configuration.ORIENTATION_PORTRAIT }
             )
-    }
+            assertThat(volumePanelState!!.orientation).isEqualTo(Configuration.ORIENTATION_PORTRAIT)
 
-    @Test
-    fun dismissingPanel_changesVisibility() {
-        with(kosmos) {
-            testScope.runTest {
-                initUnderTest()
-                assertThat(underTest.volumePanelState.value.isVisible).isTrue()
+            fakeConfigurationController.onConfigurationChanged(
+                Configuration().apply { orientation = Configuration.ORIENTATION_LANDSCAPE }
+            )
+            runCurrent()
 
-                underTest.dismissPanel()
-                runCurrent()
-
-                assertThat(underTest.volumePanelState.value.isVisible).isFalse()
-            }
+            assertThat(volumePanelState!!.orientation)
+                .isEqualTo(Configuration.ORIENTATION_LANDSCAPE)
         }
     }
 
     @Test
-    fun orientationChanges_panelOrientationChanges() {
-        with(kosmos) {
-            testScope.runTest {
-                initUnderTest()
-                val volumePanelState by collectLastValue(underTest.volumePanelState)
-                testableResources.overrideConfiguration(
-                    Configuration().apply { orientation = Configuration.ORIENTATION_PORTRAIT }
+    fun components_areReturned() =
+        test({
+            componentByKey =
+                mapOf(
+                    COMPONENT_1 to mockVolumePanelUiComponentProvider,
+                    COMPONENT_2 to mockVolumePanelUiComponentProvider,
+                    BOTTOM_BAR to mockVolumePanelUiComponentProvider,
                 )
-                assertThat(volumePanelState!!.orientation)
-                    .isEqualTo(Configuration.ORIENTATION_PORTRAIT)
-
-                fakeConfigurationController.onConfigurationChanged(
-                    Configuration().apply { orientation = Configuration.ORIENTATION_LANDSCAPE }
-                )
-                runCurrent()
-
-                assertThat(volumePanelState!!.orientation)
-                    .isEqualTo(Configuration.ORIENTATION_LANDSCAPE)
-            }
-        }
-    }
-
-    @Test
-    fun components_areReturned() {
-        with(kosmos) {
+            criteriaByKey = mapOf(COMPONENT_2 to unavailableCriteria)
+        }) {
             testScope.runTest {
-                componentByKey =
-                    mapOf(
-                        COMPONENT_1 to mockVolumePanelUiComponentProvider,
-                        COMPONENT_2 to mockVolumePanelUiComponentProvider,
-                        BOTTOM_BAR to mockVolumePanelUiComponentProvider,
-                    )
-                criteriaByKey = mapOf(COMPONENT_2 to unavailableCriteria)
-                initUnderTest()
-
                 val componentsLayout by collectLastValue(underTest.componentsLayout)
                 runCurrent()
 
@@ -124,11 +110,45 @@
                 assertThat(componentsLayout!!.bottomBarComponent.isVisible).isTrue()
             }
         }
+
+    @Test
+    fun dismissPanel_dismissesPanel() = test {
+        testScope.runTest {
+            val volumePanelState by collectLastValue(underTest.volumePanelState)
+            underTest.dismissPanel()
+            runCurrent()
+
+            assertThat(volumePanelState!!.isVisible).isFalse()
+        }
     }
 
+    @Test
+    fun dismissBroadcast_dismissesPanel() = test {
+        testScope.runTest {
+            runCurrent() // run the flows to let allow the receiver to be registered
+            val volumePanelState by collectLastValue(underTest.volumePanelState)
+            broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                applicationContext,
+                Intent(DISMISS_ACTION),
+            )
+            runCurrent()
+
+            assertThat(volumePanelState!!.isVisible).isFalse()
+        }
+    }
+
+    private fun test(setup: Kosmos.() -> Unit = {}, test: Kosmos.() -> Unit) =
+        with(kosmos) {
+            setup()
+            underTest = volumePanelViewModel
+            test()
+        }
+
     private companion object {
         const val BOTTOM_BAR: VolumePanelComponentKey = "test_bottom_bar"
         const val COMPONENT_1: VolumePanelComponentKey = "test_component:1"
         const val COMPONENT_2: VolumePanelComponentKey = "test_component:2"
+
+        const val DISMISS_ACTION = "com.android.systemui.action.DISMISS_VOLUME_PANEL_DIALOG"
     }
 }
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
index 3b6b5a0..2a8f1b5 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
@@ -66,6 +66,7 @@
 
             <com.android.systemui.bouncer.ui.BouncerMessageView
                 android:id="@+id/bouncer_message_view"
+                android:importantForAccessibility="noHideDescendants"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical" />
@@ -127,4 +128,4 @@
 
     </androidx.constraintlayout.motion.widget.MotionLayout>
 
-</com.android.keyguard.KeyguardPasswordView>
\ No newline at end of file
+</com.android.keyguard.KeyguardPasswordView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index 5aac653..76f6f59 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -31,6 +31,7 @@
 
     <com.android.systemui.bouncer.ui.BouncerMessageView
         android:id="@+id/bouncer_message_view"
+        android:importantForAccessibility="noHideDescendants"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
index 6780e57..5879c11 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
@@ -67,6 +67,7 @@
 
             <com.android.systemui.bouncer.ui.BouncerMessageView
                 android:id="@+id/bouncer_message_view"
+                android:importantForAccessibility="noHideDescendants"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical" />
@@ -107,4 +108,4 @@
 
     </androidx.constraintlayout.motion.widget.MotionLayout>
 
-</com.android.keyguard.KeyguardPatternView>
\ No newline at end of file
+</com.android.keyguard.KeyguardPatternView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index d991581..3f7b028 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -35,6 +35,7 @@
 
     <com.android.systemui.bouncer.ui.BouncerMessageView
         android:id="@+id/bouncer_message_view"
+        android:importantForAccessibility="noHideDescendants"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
index 6c79d5a..b464fb3 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
@@ -74,6 +74,7 @@
 
             <com.android.systemui.bouncer.ui.BouncerMessageView
                 android:id="@+id/bouncer_message_view"
+                android:importantForAccessibility="noHideDescendants"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical" />
@@ -241,4 +242,4 @@
 
     </androidx.constraintlayout.motion.widget.MotionLayout>
 
-</com.android.keyguard.KeyguardPINView>
\ No newline at end of file
+</com.android.keyguard.KeyguardPINView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index f3cd9e4..2158073 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -32,6 +32,7 @@
 
     <com.android.systemui.bouncer.ui.BouncerMessageView
         android:id="@+id/bouncer_message_view"
+        android:importantForAccessibility="noHideDescendants"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical" />
diff --git a/packages/SystemUI/res/drawable/ic_satellite_connected_0.xml b/packages/SystemUI/res/drawable/ic_satellite_connected_0.xml
index 045c19e..65d6e28 100644
--- a/packages/SystemUI/res/drawable/ic_satellite_connected_0.xml
+++ b/packages/SystemUI/res/drawable/ic_satellite_connected_0.xml
@@ -14,36 +14,20 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
+    android:width="16dp"
+    android:height="16dp"
+    android:viewportWidth="16.0"
+    android:viewportHeight="16.0">
     <path
-        android:pathData="M14.73,3.36L17.63,6.2C17.83,6.39 17.83,6.71 17.63,6.91L16.89,7.65C16.69,7.85 16.37,7.85 16.18,7.65L13.34,4.78C13.15,4.59 13.15,4.28 13.34,4.08L14.01,3.37C14.2,3.17 14.52,3.16 14.72,3.36H14.73ZM14.37,1C13.85,1 13.32,1.2 12.93,1.61L11.56,3.06C10.8,3.84 10.81,5.09 11.58,5.86L15.13,9.41C15.52,9.8 16.03,10 16.55,10C17.07,10 17.58,9.8 17.97,9.41L19.42,7.96C20.21,7.17 20.2,5.89 19.4,5.12L15.77,1.57C15.38,1.19 14.88,1 14.37,1Z"
+        android:pathData="M15.632,8H14.902C14.707,8 14.553,8.155 14.543,8.35C14.367,11.692 11.693,14.417 8.356,14.552C8.158,14.56 8,14.712 8,14.909V15.552C8,15.84 8.172,16.009 8.377,16C12.493,15.808 15.808,12.494 16,8.378C16.009,8.172 15.837,8 15.632,8Z"
+        android:fillAlpha="0.18"
         android:fillColor="#fff"/>
     <path
-        android:pathData="M4.73,13.36L7.63,16.2C7.83,16.39 7.83,16.71 7.63,16.91L6.89,17.65C6.69,17.85 6.37,17.85 6.18,17.65L3.34,14.78C3.15,14.59 3.15,14.28 3.34,14.08L4.01,13.37C4.2,13.17 4.52,13.16 4.72,13.36H4.73ZM4.37,11C3.85,11 3.32,11.2 2.93,11.61L1.56,13.06C0.8,13.84 0.81,15.09 1.58,15.86L5.13,19.41C5.52,19.8 6.03,20 6.55,20C7.07,20 7.58,19.8 7.97,19.41L9.42,17.96C10.21,17.17 10.2,15.89 9.4,15.12L5.77,11.57C5.38,11.19 4.88,11 4.37,11Z"
+        android:pathData="M12.643,8C12.932,8 13.105,8.175 13.09,8.382C12.903,10.892 10.892,12.903 8.382,13.09C8.175,13.105 8,12.93 8,12.723V11.993C8,11.798 8.153,11.648 8.347,11.63C10.089,11.467 11.546,10.092 11.645,8.362C11.657,8.163 11.807,8 12.006,8L12.643,8Z"
+        android:fillAlpha="0.18"
         android:fillColor="#fff"/>
     <path
-        android:pathData="M8.622,5.368L5.372,8.618L10.112,13.358C11.009,14.255 12.464,14.255 13.362,13.358C14.259,12.46 14.259,11.005 13.362,10.108L8.622,5.368Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M16.766,3.169L13.471,6.464L14.532,7.525L17.827,4.23L16.766,3.169Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M14.728,5.226L3.478,16.476L4.538,17.536L15.788,6.286L14.728,5.226Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M12.63,9.38L9.38,12.63L4.67,7.92C3.77,7.02 3.77,5.57 4.67,4.67C5.57,3.77 7.02,3.77 7.92,4.67L12.63,9.38Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M11,22.48V21.48C11,21.21 11.22,21 11.49,20.99C16.63,20.8 20.75,16.62 20.99,11.48C21,11.21 21.21,11 21.48,11H22.48C22.76,11 23,11.24 22.99,11.52C22.72,17.73 17.73,22.73 11.52,22.99C11.24,23 11,22.77 11,22.48Z"
-        android:fillAlpha="0.3"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M11,18.98V17.98C11,17.71 11.21,17.51 11.48,17.49C14.69,17.26 17.33,14.7 17.49,11.49C17.5,11.22 17.71,11.01 17.98,11.01H18.79C19.26,11.01 19.5,11.25 19.48,11.53C19.22,15.8 15.79,19.23 11.52,19.49C11.24,19.51 11,19.27 11,18.99V18.98Z"
-        android:fillAlpha="0.3"
+        android:pathData="M7.743,10.057L6.14,8.451L5.205,9.386L6.105,10.266C6.684,10.832 6.69,11.76 6.119,12.333L5.065,13.391C4.78,13.676 4.407,13.818 4.035,13.818C3.662,13.818 3.289,13.676 3.004,13.391L0.425,10.807C-0.136,10.245 -0.143,9.338 0.41,8.768L1.404,7.716C1.69,7.421 2.07,7.273 2.45,7.273C2.817,7.273 3.184,7.411 3.467,7.688L4.425,8.624L5.369,7.68L1.712,4.026C1.57,3.884 1.57,3.654 1.712,3.512L3.513,1.711C3.655,1.57 3.885,1.57 4.027,1.711L7.684,5.366L8.608,4.442L7.701,3.534C7.14,2.972 7.134,2.065 7.686,1.495L8.68,0.443C8.966,0.148 9.346,0 9.726,0C10.093,0 10.46,0.138 10.743,0.415L13.381,2.992C13.959,3.557 13.965,4.488 13.394,5.061L12.341,6.118C12.056,6.403 11.684,6.545 11.311,6.545C10.938,6.545 10.565,6.403 10.281,6.118L9.379,5.214L8.456,6.137L10.059,7.743C10.2,7.885 10.2,8.115 10.058,8.257L8.257,10.057C8.115,10.199 7.885,10.199 7.743,10.057ZM10.412,4.182L11.053,4.83C11.195,4.973 11.427,4.974 11.569,4.831L12.104,4.294C12.247,4.151 12.245,3.919 12.101,3.777L11.451,3.142L10.412,4.182ZM8.99,2.744L9.645,3.406L10.671,2.379L9.989,1.713C9.844,1.571 9.61,1.576 9.471,1.724L8.984,2.239C8.85,2.381 8.852,2.604 8.99,2.744ZM3.395,9.653L2.713,8.986C2.568,8.844 2.334,8.849 2.194,8.997L1.708,9.512C1.574,9.654 1.576,9.878 1.714,10.017L2.369,10.679L3.395,9.653ZM4.825,11.051L4.176,10.416L3.136,11.455L3.777,12.103C3.919,12.247 4.151,12.247 4.293,12.104L4.828,11.567C4.971,11.424 4.969,11.192 4.825,11.051Z"
         android:fillColor="#fff"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_satellite_connected_1.xml b/packages/SystemUI/res/drawable/ic_satellite_connected_1.xml
index 5e012ab..0399b81 100644
--- a/packages/SystemUI/res/drawable/ic_satellite_connected_1.xml
+++ b/packages/SystemUI/res/drawable/ic_satellite_connected_1.xml
@@ -16,34 +16,18 @@
   -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
+    android:width="16dp"
+    android:height="16dp"
+    android:viewportWidth="16.0"
+    android:viewportHeight="16.0">
     <path
-        android:pathData="M14.73,3.36L17.63,6.2C17.83,6.39 17.83,6.71 17.63,6.91L16.89,7.65C16.69,7.85 16.37,7.85 16.18,7.65L13.34,4.78C13.15,4.59 13.15,4.28 13.34,4.08L14.01,3.37C14.2,3.17 14.52,3.16 14.72,3.36H14.73ZM14.37,1C13.85,1 13.32,1.2 12.93,1.61L11.56,3.06C10.8,3.84 10.81,5.09 11.58,5.86L15.13,9.41C15.52,9.8 16.03,10 16.55,10C17.07,10 17.58,9.8 17.97,9.41L19.42,7.96C20.21,7.17 20.2,5.89 19.4,5.12L15.77,1.57C15.38,1.19 14.88,1 14.37,1Z"
+        android:pathData="M15.632,8H14.902C14.707,8 14.553,8.155 14.543,8.35C14.367,11.692 11.693,14.417 8.356,14.552C8.158,14.56 8,14.712 8,14.909V15.552C8,15.84 8.172,16.009 8.377,16C12.493,15.808 15.808,12.494 16,8.378C16.009,8.172 15.837,8 15.632,8Z"
+        android:fillAlpha="0.18"
         android:fillColor="#fff"/>
     <path
-        android:pathData="M4.73,13.36L7.63,16.2C7.83,16.39 7.83,16.71 7.63,16.91L6.89,17.65C6.69,17.85 6.37,17.85 6.18,17.65L3.34,14.78C3.15,14.59 3.15,14.28 3.34,14.08L4.01,13.37C4.2,13.17 4.52,13.16 4.72,13.36H4.73ZM4.37,11C3.85,11 3.32,11.2 2.93,11.61L1.56,13.06C0.8,13.84 0.81,15.09 1.58,15.86L5.13,19.41C5.52,19.8 6.03,20 6.55,20C7.07,20 7.58,19.8 7.97,19.41L9.42,17.96C10.21,17.17 10.2,15.89 9.4,15.12L5.77,11.57C5.38,11.19 4.88,11 4.37,11Z"
+        android:pathData="M12.643,8C12.932,8 13.105,8.175 13.09,8.382C12.903,10.892 10.892,12.903 8.382,13.09C8.175,13.105 8,12.93 8,12.723V11.993C8,11.798 8.153,11.648 8.347,11.63C10.089,11.467 11.546,10.092 11.645,8.362C11.657,8.163 11.807,8 12.006,8L12.643,8Z"
         android:fillColor="#fff"/>
     <path
-        android:pathData="M8.622,5.368L5.372,8.618L10.112,13.358C11.009,14.255 12.464,14.255 13.362,13.358C14.259,12.46 14.259,11.005 13.362,10.108L8.622,5.368Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M16.766,3.169L13.471,6.464L14.532,7.525L17.827,4.23L16.766,3.169Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M14.728,5.226L3.478,16.476L4.538,17.536L15.788,6.286L14.728,5.226Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M12.63,9.38L9.38,12.63L4.67,7.92C3.77,7.02 3.77,5.57 4.67,4.67C5.57,3.77 7.02,3.77 7.92,4.67L12.63,9.38Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M11,22.48V21.48C11,21.21 11.22,21 11.49,20.99C16.63,20.8 20.75,16.62 20.99,11.48C21,11.21 21.21,11 21.48,11H22.48C22.76,11 23,11.24 22.99,11.52C22.72,17.73 17.73,22.73 11.52,22.99C11.24,23 11,22.77 11,22.48Z"
-        android:fillAlpha="0.3"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M11,18.98V17.98C11,17.71 11.21,17.51 11.48,17.49C14.69,17.26 17.33,14.7 17.49,11.49C17.5,11.22 17.71,11.01 17.98,11.01H18.79C19.26,11.01 19.5,11.25 19.48,11.53C19.22,15.8 15.79,19.23 11.52,19.49C11.24,19.51 11,19.27 11,18.99V18.98Z"
+        android:pathData="M7.743,10.057L6.14,8.451L5.205,9.386L6.105,10.266C6.684,10.832 6.69,11.76 6.119,12.333L5.065,13.391C4.78,13.676 4.407,13.818 4.035,13.818C3.662,13.818 3.289,13.676 3.004,13.391L0.425,10.807C-0.136,10.245 -0.143,9.338 0.41,8.768L1.404,7.716C1.69,7.421 2.07,7.273 2.45,7.273C2.817,7.273 3.184,7.411 3.467,7.688L4.425,8.624L5.369,7.68L1.712,4.026C1.57,3.884 1.57,3.654 1.712,3.512L3.513,1.711C3.655,1.57 3.885,1.57 4.027,1.711L7.684,5.366L8.608,4.442L7.701,3.534C7.14,2.972 7.134,2.065 7.686,1.495L8.68,0.443C8.966,0.148 9.346,0 9.726,0C10.093,0 10.46,0.138 10.743,0.415L13.381,2.992C13.959,3.557 13.965,4.488 13.394,5.061L12.341,6.118C12.056,6.403 11.684,6.545 11.311,6.545C10.938,6.545 10.565,6.403 10.281,6.118L9.379,5.214L8.456,6.137L10.059,7.743C10.2,7.885 10.2,8.115 10.058,8.257L8.257,10.057C8.115,10.199 7.885,10.199 7.743,10.057ZM10.412,4.182L11.053,4.83C11.195,4.973 11.427,4.974 11.569,4.831L12.104,4.294C12.247,4.151 12.245,3.919 12.101,3.777L11.451,3.142L10.412,4.182ZM8.99,2.744L9.645,3.406L10.671,2.379L9.989,1.713C9.844,1.571 9.61,1.576 9.471,1.724L8.984,2.239C8.85,2.381 8.852,2.604 8.99,2.744ZM3.395,9.653L2.713,8.986C2.568,8.844 2.334,8.849 2.194,8.997L1.708,9.512C1.574,9.654 1.576,9.878 1.714,10.017L2.369,10.679L3.395,9.653ZM4.825,11.051L4.176,10.416L3.136,11.455L3.777,12.103C3.919,12.247 4.151,12.247 4.293,12.104L4.828,11.567C4.971,11.424 4.969,11.192 4.825,11.051Z"
         android:fillColor="#fff"/>
 </vector>
-
diff --git a/packages/SystemUI/res/drawable/ic_satellite_connected_2.xml b/packages/SystemUI/res/drawable/ic_satellite_connected_2.xml
index d8a9a70..b0acbdc 100644
--- a/packages/SystemUI/res/drawable/ic_satellite_connected_2.xml
+++ b/packages/SystemUI/res/drawable/ic_satellite_connected_2.xml
@@ -16,32 +16,17 @@
   -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
+    android:width="16dp"
+    android:height="16dp"
+    android:viewportWidth="16.0"
+    android:viewportHeight="16.0">
     <path
-        android:pathData="M14.73,3.36L17.63,6.2C17.83,6.39 17.83,6.71 17.63,6.91L16.89,7.65C16.69,7.85 16.37,7.85 16.18,7.65L13.34,4.78C13.15,4.59 13.15,4.28 13.34,4.08L14.01,3.37C14.2,3.17 14.52,3.16 14.72,3.36H14.73ZM14.37,1C13.85,1 13.32,1.2 12.93,1.61L11.56,3.06C10.8,3.84 10.81,5.09 11.58,5.86L15.13,9.41C15.52,9.8 16.03,10 16.55,10C17.07,10 17.58,9.8 17.97,9.41L19.42,7.96C20.21,7.17 20.2,5.89 19.4,5.12L15.77,1.57C15.38,1.19 14.88,1 14.37,1Z"
+        android:pathData="M15.632,8H14.902C14.707,8 14.553,8.155 14.543,8.35C14.367,11.692 11.693,14.417 8.356,14.552C8.158,14.56 8,14.712 8,14.909V15.552C8,15.84 8.172,16.009 8.377,16C12.493,15.808 15.808,12.494 16,8.378C16.009,8.172 15.837,8 15.632,8Z"
         android:fillColor="#fff"/>
     <path
-        android:pathData="M4.73,13.36L7.63,16.2C7.83,16.39 7.83,16.71 7.63,16.91L6.89,17.65C6.69,17.85 6.37,17.85 6.18,17.65L3.34,14.78C3.15,14.59 3.15,14.28 3.34,14.08L4.01,13.37C4.2,13.17 4.52,13.16 4.72,13.36H4.73ZM4.37,11C3.85,11 3.32,11.2 2.93,11.61L1.56,13.06C0.8,13.84 0.81,15.09 1.58,15.86L5.13,19.41C5.52,19.8 6.03,20 6.55,20C7.07,20 7.58,19.8 7.97,19.41L9.42,17.96C10.21,17.17 10.2,15.89 9.4,15.12L5.77,11.57C5.38,11.19 4.88,11 4.37,11Z"
+        android:pathData="M12.643,8C12.932,8 13.105,8.175 13.09,8.382C12.903,10.892 10.892,12.903 8.382,13.09C8.175,13.105 8,12.93 8,12.723V11.993C8,11.798 8.153,11.648 8.347,11.63C10.089,11.467 11.546,10.092 11.645,8.362C11.657,8.163 11.807,8 12.006,8L12.643,8Z"
         android:fillColor="#fff"/>
     <path
-        android:pathData="M8.622,5.368L5.372,8.618L10.112,13.358C11.009,14.255 12.464,14.255 13.362,13.358C14.259,12.46 14.259,11.005 13.362,10.108L8.622,5.368Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M16.766,3.169L13.471,6.464L14.532,7.525L17.827,4.23L16.766,3.169Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M14.728,5.226L3.478,16.476L4.538,17.536L15.788,6.286L14.728,5.226Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M12.63,9.38L9.38,12.63L4.67,7.92C3.77,7.02 3.77,5.57 4.67,4.67C5.57,3.77 7.02,3.77 7.92,4.67L12.63,9.38Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M11,22.48V21.48C11,21.21 11.22,21 11.49,20.99C16.63,20.8 20.75,16.62 20.99,11.48C21,11.21 21.21,11 21.48,11H22.48C22.76,11 23,11.24 22.99,11.52C22.72,17.73 17.73,22.73 11.52,22.99C11.24,23 11,22.77 11,22.48Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M11,18.98V17.98C11,17.71 11.21,17.51 11.48,17.49C14.69,17.26 17.33,14.7 17.49,11.49C17.5,11.22 17.71,11.01 17.98,11.01H18.79C19.26,11.01 19.5,11.25 19.48,11.53C19.22,15.8 15.79,19.23 11.52,19.49C11.24,19.51 11,19.27 11,18.99V18.98Z"
+        android:pathData="M7.743,10.057L6.14,8.451L5.205,9.386L6.105,10.266C6.684,10.832 6.69,11.76 6.119,12.333L5.065,13.391C4.78,13.676 4.407,13.818 4.035,13.818C3.662,13.818 3.289,13.676 3.004,13.391L0.425,10.807C-0.136,10.245 -0.143,9.338 0.41,8.768L1.404,7.716C1.69,7.421 2.07,7.273 2.45,7.273C2.817,7.273 3.184,7.411 3.467,7.688L4.425,8.624L5.369,7.68L1.712,4.026C1.57,3.884 1.57,3.654 1.712,3.512L3.513,1.711C3.655,1.57 3.885,1.57 4.027,1.711L7.684,5.366L8.608,4.442L7.701,3.534C7.14,2.972 7.134,2.065 7.686,1.495L8.68,0.443C8.966,0.148 9.346,0 9.726,0C10.093,0 10.46,0.138 10.743,0.415L13.381,2.992C13.959,3.557 13.965,4.488 13.394,5.061L12.341,6.118C12.056,6.403 11.684,6.545 11.311,6.545C10.938,6.545 10.565,6.403 10.281,6.118L9.379,5.214L8.456,6.137L10.059,7.743C10.2,7.885 10.2,8.115 10.058,8.257L8.257,10.057C8.115,10.199 7.885,10.199 7.743,10.057ZM10.412,4.182L11.053,4.83C11.195,4.973 11.427,4.974 11.569,4.831L12.104,4.294C12.247,4.151 12.245,3.919 12.101,3.777L11.451,3.142L10.412,4.182ZM8.99,2.744L9.645,3.406L10.671,2.379L9.989,1.713C9.844,1.571 9.61,1.576 9.471,1.724L8.984,2.239C8.85,2.381 8.852,2.604 8.99,2.744ZM3.395,9.653L2.713,8.986C2.568,8.844 2.334,8.849 2.194,8.997L1.708,9.512C1.574,9.654 1.576,9.878 1.714,10.017L2.369,10.679L3.395,9.653ZM4.825,11.051L4.176,10.416L3.136,11.455L3.777,12.103C3.919,12.247 4.151,12.247 4.293,12.104L4.828,11.567C4.971,11.424 4.969,11.192 4.825,11.051Z"
         android:fillColor="#fff"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_satellite_not_connected.xml b/packages/SystemUI/res/drawable/ic_satellite_not_connected.xml
index a80d3b4..2cab043 100644
--- a/packages/SystemUI/res/drawable/ic_satellite_not_connected.xml
+++ b/packages/SystemUI/res/drawable/ic_satellite_not_connected.xml
@@ -16,28 +16,11 @@
   -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0"
-    android:alpha="0.3"
-    >
+    android:width="14dp"
+    android:height="14dp"
+    android:viewportWidth="14.0"
+    android:viewportHeight="14.0">
     <path
-        android:pathData="M14.73,3.36L17.63,6.2C17.83,6.39 17.83,6.71 17.63,6.91L16.89,7.65C16.69,7.85 16.37,7.85 16.18,7.65L13.34,4.78C13.15,4.59 13.15,4.28 13.34,4.08L14.01,3.37C14.2,3.17 14.52,3.16 14.72,3.36H14.73ZM14.37,1C13.85,1 13.32,1.2 12.93,1.61L11.56,3.06C10.8,3.84 10.81,5.09 11.58,5.86L15.13,9.41C15.52,9.8 16.03,10 16.55,10C17.07,10 17.58,9.8 17.97,9.41L19.42,7.96C20.21,7.17 20.2,5.89 19.4,5.12L15.77,1.57C15.38,1.19 14.88,1 14.37,1Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M4.73,13.36L7.63,16.2C7.83,16.39 7.83,16.71 7.63,16.91L6.89,17.65C6.69,17.85 6.37,17.85 6.18,17.65L3.34,14.78C3.15,14.59 3.15,14.28 3.34,14.08L4.01,13.37C4.2,13.17 4.52,13.16 4.72,13.36H4.73ZM4.37,11C3.85,11 3.32,11.2 2.93,11.61L1.56,13.06C0.8,13.84 0.81,15.09 1.58,15.86L5.13,19.41C5.52,19.8 6.03,20 6.55,20C7.07,20 7.58,19.8 7.97,19.41L9.42,17.96C10.21,17.17 10.2,15.89 9.4,15.12L5.77,11.57C5.38,11.19 4.88,11 4.37,11Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M8.622,5.368L5.372,8.618L10.112,13.358C11.009,14.255 12.464,14.255 13.362,13.358C14.259,12.46 14.259,11.005 13.362,10.108L8.622,5.368Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M16.766,3.169L13.471,6.464L14.532,7.525L17.827,4.23L16.766,3.169Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M14.728,5.226L3.478,16.476L4.538,17.536L15.788,6.286L14.728,5.226Z"
-        android:fillColor="#fff"/>
-    <path
-        android:pathData="M12.63,9.38L9.38,12.63L4.67,7.92C3.77,7.02 3.77,5.57 4.67,4.67C5.57,3.77 7.02,3.77 7.92,4.67L12.63,9.38Z"
+        android:pathData="M7.743,10.057L6.14,8.451L5.205,9.386L6.105,10.266C6.684,10.832 6.69,11.76 6.119,12.333L5.065,13.391C4.78,13.676 4.407,13.818 4.035,13.818C3.662,13.818 3.289,13.676 3.004,13.391L0.425,10.807C-0.136,10.245 -0.143,9.338 0.41,8.768L1.404,7.716C1.69,7.421 2.07,7.273 2.45,7.273C2.817,7.273 3.184,7.411 3.467,7.688L4.425,8.624L5.369,7.68L1.712,4.026C1.57,3.884 1.57,3.654 1.712,3.512L3.513,1.711C3.655,1.57 3.885,1.57 4.027,1.711L7.684,5.366L8.608,4.442L7.701,3.534C7.14,2.972 7.134,2.065 7.686,1.495L8.68,0.443C8.966,0.148 9.346,0 9.726,0C10.093,0 10.46,0.138 10.743,0.415L13.381,2.992C13.959,3.557 13.965,4.488 13.394,5.061L12.341,6.118C12.056,6.403 11.684,6.545 11.311,6.545C10.938,6.545 10.565,6.403 10.281,6.118L9.379,5.214L8.456,6.137L10.059,7.743C10.2,7.885 10.2,8.115 10.058,8.257L8.257,10.057C8.115,10.199 7.885,10.199 7.743,10.057ZM10.412,4.182L11.053,4.83C11.195,4.973 11.427,4.974 11.569,4.831L12.104,4.294C12.247,4.151 12.245,3.919 12.101,3.777L11.451,3.142L10.412,4.182ZM8.99,2.744L9.645,3.406L10.671,2.379L9.989,1.713C9.844,1.571 9.61,1.576 9.471,1.724L8.984,2.239C8.85,2.381 8.852,2.604 8.99,2.744ZM3.395,9.653L2.713,8.986C2.568,8.844 2.334,8.849 2.194,8.997L1.708,9.512C1.574,9.654 1.576,9.878 1.714,10.017L2.369,10.679L3.395,9.653ZM4.825,11.051L4.176,10.416L3.136,11.455L3.777,12.103C3.919,12.247 4.151,12.247 4.293,12.104L4.828,11.567C4.971,11.424 4.969,11.192 4.825,11.051Z"
         android:fillColor="#fff"/>
 </vector>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 19273ec..6bfd088 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -461,10 +461,11 @@
          This name is in the ComponentName flattened format (package/class)  -->
     <string name="config_screenshotEditor" translatable="false"></string>
 
-    <!-- ComponentName for the file browsing app that the system would expect to be used in work
-         profile. The icon for this app will be shown to the user when informing them that a
-         screenshot has been saved to work profile. If blank, a default icon will be shown. -->
-    <string name="config_sceenshotWorkProfileFilesApp" translatable="false"></string>
+    <!-- ComponentName for the file browsing app that the system would expect to be used for
+         screenshots. The icon for this app will be shown to the user when informing them that a
+         screenshot has been saved to a different profile (e.g. work profile). If blank, a default
+         icon will be shown. -->
+    <string name="config_screenshotFilesApp" translatable="false"></string>
 
     <!-- The component name of the screenshot editing activity that provides the App Clips flow.
          The App Clips flow includes taking a screenshot, showing user screenshot cropping activity
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 9c6e5f8..dcdd4f0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -257,6 +257,8 @@
     <string name="screenshot_right_boundary_pct">Right boundary <xliff:g id="percent" example="50">%1$d</xliff:g> percent</string>
     <!-- Notification displayed when a screenshot is saved in a work profile. [CHAR LIMIT=NONE] -->
     <string name="screenshot_work_profile_notification">Saved in <xliff:g id="app" example="Files">%1$s</xliff:g> in the work profile</string>
+    <!-- Notification displayed when a screenshot is saved in the private profile. [CHAR LIMIT=NONE] -->
+    <string name="screenshot_private_profile_notification">Saved in <xliff:g id="app" example="Files">%1$s</xliff:g> in the private profile</string>
     <!-- Default name referring to the app on the device that lets the user browse stored files. [CHAR LIMIT=NONE] -->
     <string name="screenshot_default_files_app_name">Files</string>
     <!-- A notice shown to the user to indicate that an app has detected the screenshot that the user has just taken. [CHAR LIMIT=75] -->
@@ -1182,6 +1184,8 @@
     <string name="accessibility_action_label_edit_widgets">Customize widgets</string>
     <!-- Accessibility content description for communal hub. [CHAR LIMIT=NONE] -->
     <string name="accessibility_content_description_for_communal_hub">Widgets on lock screen</string>
+    <!-- Label for accessibility action to select a widget in edit mode. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_label_select_widget">select widget</string>
 
     <!-- Related to user switcher --><skip/>
 
@@ -1722,9 +1726,7 @@
     <string name="accessibility_status_bar_satellite_available">Satellite, connection available</string>
 
     <!-- Text displayed indicating that the user is connected to a satellite signal. -->
-    <string name="satellite_connected_carrier_text">Connected to satellite</string>
-    <!-- Text displayed indicating that the user is not connected to a satellite signal. -->
-    <string name="satellite_not_connected_carrier_text">Not connected to satellite</string>
+    <string name="satellite_connected_carrier_text">Satellite SOS</string>
 
     <!-- Accessibility label for managed profile icon (not shown on screen) [CHAR LIMIT=NONE] -->
     <string name="accessibility_managed_profile">Work profile</string>
@@ -2264,6 +2266,9 @@
     <!-- Accessibility description when QS tile is to be added, indicating the destination position [CHAR LIMIT=NONE] -->
     <string name="accessibility_qs_edit_tile_add_to_position">Add to position <xliff:g id="position" example="5">%1$d</xliff:g></string>
 
+    <!-- Accessibility description when QS tile would be added or moved, but the current position is not valid for adding or moving to [CHAR LIMIT=NONE] -->
+    <string name="accessibilit_qs_edit_tile_add_move_invalid_position">Position invalid.</string>
+
     <!-- Accessibility description indicating the currently selected tile's position. Only used for tiles that are currently in use [CHAR LIMIT=NONE] -->
     <string name="accessibility_qs_edit_position">Position <xliff:g id="position" example="5">%1$d</xliff:g></string>
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index b916fc2..91fb688 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -81,7 +81,6 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardWmStateRefactor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.shared.RefactorKeyguardDismissIntent;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
@@ -304,7 +303,7 @@
          */
         @Override
         public void finish(int targetUserId) {
-            if (!RefactorKeyguardDismissIntent.isEnabled()) {
+            if (!SceneContainerFlag.isEnabled()) {
                 // If there's a pending runnable because the user interacted with a widget
                 // and we're leaving keyguard, then run it.
                 boolean deferKeyguardDone = false;
@@ -614,7 +613,7 @@
      * @param action callback to be invoked when keyguard disappear animation completes.
      */
     public void setOnDismissAction(ActivityStarter.OnDismissAction action, Runnable cancelAction) {
-        if (RefactorKeyguardDismissIntent.isEnabled()) {
+        if (SceneContainerFlag.isEnabled()) {
             return;
         }
         if (mCancelAction != null) {
@@ -908,7 +907,7 @@
             mUiEventLogger.log(uiEvent, getSessionId());
         }
 
-        if (RefactorKeyguardDismissIntent.isEnabled()) {
+        if (SceneContainerFlag.isEnabled()) {
             if (authenticatedWithPrimaryAuth) {
                 mPrimaryBouncerInteractor.get()
                         .notifyKeyguardAuthenticatedPrimaryAuth(targetUserId);
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index b8af59d..4c9af66 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -121,6 +121,9 @@
             SystemProperties.getBoolean("debug.disable_screen_decorations", false);
     private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
             SystemProperties.getBoolean("debug.screenshot_rounded_corners", false);
+
+    private static final boolean sToolkitSetFrameRateReadOnly =
+            android.view.flags.Flags.toolkitSetFrameRateReadOnly();
     private boolean mDebug = DEBUG_SCREENSHOT_ROUNDED_CORNERS;
     private int mDebugColor = Color.RED;
 
@@ -892,6 +895,10 @@
         lp.width = MATCH_PARENT;
         lp.height = MATCH_PARENT;
         lp.setTitle("ScreenDecorHwcOverlay");
+        if (sToolkitSetFrameRateReadOnly) {
+            lp.setFrameRateBoostOnTouchEnabled(false);
+            lp.setFrameRatePowerSavingsBalanced(false);
+        }
         lp.gravity = Gravity.TOP | Gravity.START;
         if (!mDebug) {
             lp.setColorMode(ActivityInfo.COLOR_MODE_A8);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index c6716e4..68a69d3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -192,6 +192,7 @@
     private final ShadeController mShadeController;
     private final Lazy<PanelExpansionInteractor> mPanelExpansionInteractor;
     private final StatusBarWindowCallback mNotificationShadeCallback;
+    private final ScreenshotHelper mScreenshotHelper;
     private boolean mDismissNotificationShadeActionRegistered;
 
     @Inject
@@ -221,6 +222,7 @@
                 (keyguardShowing, keyguardOccluded, keyguardGoingAway, bouncerShowing, mDozing,
                         panelExpanded, isDreaming) ->
                         registerOrUnregisterDismissNotificationShadeAction();
+        mScreenshotHelper = new ScreenshotHelper(mContext);
     }
 
     @Override
@@ -516,8 +518,7 @@
     }
 
     private void handleTakeScreenshot() {
-        ScreenshotHelper screenshotHelper = new ScreenshotHelper(mContext);
-        screenshotHelper.takeScreenshot(
+        mScreenshotHelper.takeScreenshot(
                 SCREENSHOT_ACCESSIBILITY_ACTIONS, new Handler(Looper.getMainLooper()), null);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index a081ecc..db251fd 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName
 import android.os.UserHandle
+import android.view.View
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
@@ -37,7 +38,7 @@
 ) {
     val currentScene: Flow<SceneKey> = communalInteractor.desiredScene
 
-    /** Whether communal hub can be focused by accessibility tools. */
+    /** Whether communal hub should be focused by accessibility tools. */
     open val isFocusable: Flow<Boolean> = MutableStateFlow(false)
 
     /** Whether widgets are currently being re-ordered. */
@@ -49,6 +50,9 @@
     val selectedKey: StateFlow<String?>
         get() = _selectedKey
 
+    /** Accessibility delegate to be set on CommunalAppWidgetHostView. */
+    open val widgetAccessibilityDelegate: View.AccessibilityDelegate? = null
+
     fun signalUserInteraction() {
         communalInteractor.signalUserInteraction()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 24ea7b6..1120466 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -16,11 +16,15 @@
 
 package com.android.systemui.communal.ui.viewmodel
 
+import android.content.res.Resources
+import android.view.View
+import android.view.accessibility.AccessibilityNodeInfo
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -31,7 +35,9 @@
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.controls.ui.view.MediaHostState
 import com.android.systemui.media.dagger.MediaModule
+import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import javax.inject.Inject
 import javax.inject.Named
 import kotlinx.coroutines.CoroutineScope
@@ -56,6 +62,7 @@
 @Inject
 constructor(
     @Application private val scope: CoroutineScope,
+    @Main private val resources: Resources,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val communalInteractor: CommunalInteractor,
     tutorialInteractor: CommunalTutorialInteractor,
@@ -108,6 +115,25 @@
             }
             .distinctUntilChanged()
 
+    override val widgetAccessibilityDelegate =
+        object : View.AccessibilityDelegate() {
+            override fun onInitializeAccessibilityNodeInfo(
+                host: View,
+                info: AccessibilityNodeInfo
+            ) {
+                super.onInitializeAccessibilityNodeInfo(host, info)
+                // Hint user to long press in order to enter edit mode
+                info.addAction(
+                    AccessibilityNodeInfo.AccessibilityAction(
+                        AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
+                        resources
+                            .getString(R.string.accessibility_action_label_edit_widgets)
+                            .lowercase()
+                    )
+                )
+            }
+        }
+
     private val _isEnableWidgetDialogShowing: MutableStateFlow<Boolean> = MutableStateFlow(false)
     val isEnableWidgetDialogShowing: Flow<Boolean> = _isEnableWidgetDialogShowing.asStateFlow()
 
@@ -206,6 +232,14 @@
         return !shadeInteractor.isAnyFullyExpanded.value
     }
 
+    /**
+     * Whether touches should be disabled in communal.
+     *
+     * This is needed because the notification shade does not block touches in blank areas and these
+     * fall through to the glanceable hub, which we don't want.
+     */
+    val touchesAllowed: Flow<Boolean> = not(shadeInteractor.isAnyFullyExpanded)
+
     companion object {
         const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
index 0fe9bf4..840c3a8 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
@@ -22,10 +22,8 @@
 import android.graphics.Rect
 import android.view.View
 import android.view.ViewOutlineProvider
-import android.view.accessibility.AccessibilityNodeInfo
 import com.android.systemui.animation.LaunchableView
 import com.android.systemui.animation.LaunchableViewDelegate
-import com.android.systemui.res.R
 
 /** AppWidgetHostView that displays in communal hub with support for rounded corners. */
 class CommunalAppWidgetHostView(context: Context) : AppWidgetHostView(context), LaunchableView {
@@ -44,25 +42,6 @@
     init {
         enforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context)
         enforcedRectangle = Rect()
-
-        accessibilityDelegate =
-            object : AccessibilityDelegate() {
-                override fun onInitializeAccessibilityNodeInfo(
-                    host: View,
-                    info: AccessibilityNodeInfo
-                ) {
-                    super.onInitializeAccessibilityNodeInfo(host, info)
-                    // Hint user to long press in order to enter edit mode
-                    info.addAction(
-                        AccessibilityNodeInfo.AccessibilityAction(
-                            AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
-                            context.getString(
-                                R.string.accessibility_action_label_edit_widgets
-                            ).lowercase()
-                        )
-                    )
-                }
-            }
     }
 
     override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index 2fa42ec..7ced932 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -24,7 +24,6 @@
 import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver;
 import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
 import com.android.systemui.screenshot.SmartActionsReceiver;
-import com.android.systemui.volume.VolumePanelDialogReceiver;
 
 import dagger.Binds;
 import dagger.Module;
@@ -59,15 +58,6 @@
      */
     @Binds
     @IntoMap
-    @ClassKey(VolumePanelDialogReceiver.class)
-    public abstract BroadcastReceiver bindVolumePanelDialogReceiver(
-            VolumePanelDialogReceiver broadcastReceiver);
-
-    /**
-     *
-     */
-    @Binds
-    @IntoMap
     @ClassKey(PeopleSpaceWidgetPinnedReceiver.class)
     public abstract BroadcastReceiver bindPeopleSpaceWidgetPinnedReceiver(
             PeopleSpaceWidgetPinnedReceiver broadcastReceiver);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 1fba737..c2843d8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -168,11 +168,13 @@
             keyguardInteractor.isKeyguardOccluded
                 .filterRelevantKeyguardStateAnd { isOccluded -> isOccluded }
                 .collect {
-                    startTransitionTo(
-                        toState = KeyguardState.OCCLUDED,
-                        modeOnCanceled = TransitionModeOnCanceled.RESET,
-                        ownerReason = "isOccluded = true",
-                    )
+                    if (!maybeHandleInsecurePowerGesture()) {
+                        startTransitionTo(
+                            toState = KeyguardState.OCCLUDED,
+                            modeOnCanceled = TransitionModeOnCanceled.RESET,
+                            ownerReason = "isOccluded = true",
+                        )
+                    }
                 }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
index 4abd6c6..f385671 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
@@ -77,7 +77,9 @@
         // transition, to ensure we don't transition while moving between, for example,
         // *_BOUNCER -> LOCKSCREEN.
         return powerInteractor.detailedWakefulness.value.powerButtonLaunchGestureTriggered &&
-            KeyguardState.deviceIsAsleepInState(transitionInteractor.getStartedState())
+            KeyguardState.deviceIsAsleepInState(
+                transitionInteractor.currentTransitionInfoInternal.value.to
+            )
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 2850165..b2a24ca 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -225,10 +225,12 @@
         if (!KeyguardWmStateRefactor.isEnabled) {
             scope.launch {
                 keyguardInteractor.onCameraLaunchDetected.filterRelevantKeyguardState().collect {
-                    startTransitionTo(
-                        toState = KeyguardState.OCCLUDED,
-                        modeOnCanceled = TransitionModeOnCanceled.RESET,
-                    )
+                    if (!maybeHandleInsecurePowerGesture()) {
+                        startTransitionTo(
+                            toState = KeyguardState.OCCLUDED,
+                            modeOnCanceled = TransitionModeOnCanceled.RESET,
+                        )
+                    }
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
index f5cd767..63bfba6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
@@ -19,10 +19,10 @@
 
 import android.animation.FloatEvaluator
 import android.animation.IntEvaluator
-import com.android.systemui.common.ui.data.repository.ConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import com.android.systemui.statusbar.phone.hideAffordancesRequest
 import javax.inject.Inject
@@ -38,17 +38,16 @@
 class UdfpsKeyguardInteractor
 @Inject
 constructor(
-    configRepo: ConfigurationRepository,
     burnInInteractor: BurnInInteractor,
     keyguardInteractor: KeyguardInteractor,
-    shadeRepository: ShadeRepository,
+    shadeInteractor: ShadeInteractor,
+    shadeLockscreenInteractor: ShadeLockscreenInteractor,
     dialogManager: SystemUIDialogManager,
 ) {
     private val intEvaluator = IntEvaluator()
     private val floatEvaluator = FloatEvaluator()
 
     val dozeAmount = keyguardInteractor.dozeAmount
-    val scaleForResolution = configRepo.scaleForResolution
 
     /** Burn-in offsets for the UDFPS view to mitigate burn-in on AOD. */
     val burnInOffsets: Flow<Offsets> =
@@ -68,13 +67,14 @@
     val dialogHideAffordancesRequest: Flow<Boolean> = dialogManager.hideAffordancesRequest
 
     val qsProgress: Flow<Float> =
-        shadeRepository.qsExpansion // swipe from top of LS
+        shadeInteractor.qsExpansion // swipe from top of LS
             .map { (it * 2).coerceIn(0f, 1f) }
             .onStart { emit(0f) }
 
     val shadeExpansion: Flow<Float> =
         combine(
-                shadeRepository.udfpsTransitionToFullShadeProgress, // swipe from middle of LS
+                shadeLockscreenInteractor
+                    .udfpsTransitionToFullShadeProgress, // swipe from middle of LS
                 keyguardInteractor.statusBarState, // quick swipe from middle of LS
             ) { shadeProgress, statusBarState ->
                 if (statusBarState == StatusBarState.SHADE_LOCKED) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/RefactorKeyguardDismissIntent.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/RefactorKeyguardDismissIntent.kt
deleted file mode 100644
index a43eb71..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/RefactorKeyguardDismissIntent.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.shared
-
-import com.android.systemui.Flags
-import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
-
-/** Helper for reading or using the refactor_keyguard_dismiss_intent flag. */
-@Suppress("NOTHING_TO_INLINE")
-object RefactorKeyguardDismissIntent {
-    /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT
-
-    /** A token used for dependency declaration */
-    val token: FlagToken
-        get() = FlagToken(FLAG_NAME, isEnabled)
-
-    /** Is the refactor enabled */
-    @JvmStatic
-    inline val isEnabled
-        get() = Flags.refactorKeyguardDismissIntent()
-
-    /**
-     * 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 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/keyguard/ui/binder/KeyguardDismissActionBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
index 93b3ba5..b293027 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
@@ -20,8 +20,8 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
-import com.android.systemui.keyguard.shared.RefactorKeyguardDismissIntent
 import com.android.systemui.log.core.LogLevel
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -40,7 +40,7 @@
 ) : CoreStartable {
 
     override fun start() {
-        if (!RefactorKeyguardDismissIntent.isEnabled) {
+        if (!SceneContainerFlag.isEnabled) {
             return
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
index f77d012..ac24591 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
@@ -22,9 +22,9 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor
-import com.android.systemui.keyguard.shared.RefactorKeyguardDismissIntent
 import com.android.systemui.keyguard.shared.model.KeyguardDone
 import com.android.systemui.log.core.LogLevel
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -44,7 +44,7 @@
 ) : CoreStartable {
 
     override fun start() {
-        if (!RefactorKeyguardDismissIntent.isEnabled) {
+        if (!SceneContainerFlag.isEnabled) {
             return
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
index 7e39a884..adc090d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
@@ -57,5 +57,15 @@
         )
     }
 
+    fun notificationAlpha(viewState: ViewStateAccessor): Flow<Float> {
+        var startAlpha = 1f
+        return transitionAnimation.sharedFlow(
+            duration = 200.milliseconds,
+            onStart = { startAlpha = viewState.alpha() },
+            onStep = { startAlpha },
+            onFinish = { 1f },
+        )
+    }
+
     override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
index 24429fa..e0a3af6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
@@ -19,11 +19,11 @@
 import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
-import com.android.systemui.keyguard.shared.RefactorKeyguardDismissIntent
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.ScrimAlpha
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import dagger.Lazy
@@ -48,7 +48,7 @@
 ) {
     /** Common fade for scrim alpha values during *BOUNCER->GONE */
     fun scrimAlpha(duration: Duration, fromState: KeyguardState): Flow<ScrimAlpha> {
-        return if (RefactorKeyguardDismissIntent.isEnabled) {
+        return if (SceneContainerFlag.isEnabled) {
             keyguardDismissActionInteractor
                 .get()
                 .willAnimateDismissActionOnLockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index a08a234..b1fa710 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -20,11 +20,11 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
-import com.android.systemui.keyguard.shared.RefactorKeyguardDismissIntent
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.shared.model.ScrimAlpha
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import dagger.Lazy
 import javax.inject.Inject
@@ -80,7 +80,7 @@
 
     /** Bouncer container alpha */
     val bouncerAlpha: Flow<Float> =
-        if (RefactorKeyguardDismissIntent.isEnabled) {
+        if (SceneContainerFlag.isEnabled) {
             keyguardDismissActionInteractor
                 .get()
                 .willAnimateDismissActionOnLockscreen
@@ -104,7 +104,7 @@
 
     /** Lockscreen alpha */
     val lockscreenAlpha: Flow<Float> =
-        if (RefactorKeyguardDismissIntent.isEnabled) {
+        if (SceneContainerFlag.isEnabled) {
             keyguardDismissActionInteractor
                 .get()
                 .willAnimateDismissActionOnLockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index 4e77d13..6a6eba1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -51,8 +51,7 @@
     fun isRemoteResumeAllowed() = featureFlags.isEnabled(Flags.MEDIA_REMOTE_RESUME)
 
     /** Check whether to use scene framework */
-    fun isSceneContainerEnabled() =
-        SceneContainerFlag.isEnabled && MediaInSceneContainerFlag.isEnabled
+    fun isSceneContainerEnabled() = SceneContainerFlag.isEnabled
 
     /** Check whether to use media refactor code */
     fun isMediaControlsRefactorEnabled() = MediaControlsRefactorFlag.isEnabled
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt
deleted file mode 100644
index 77279f2..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaInSceneContainerFlag.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media.controls.util
-
-import com.android.systemui.Flags
-import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
-
-/** Helper for reading or using the media_in_scene_container flag state. */
-@Suppress("NOTHING_TO_INLINE")
-object MediaInSceneContainerFlag {
-    /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_MEDIA_IN_SCENE_CONTAINER
-
-    /** A token used for dependency declaration */
-    val token: FlagToken
-        get() = FlagToken(FLAG_NAME, isEnabled)
-
-    /** Is the flag enabled? */
-    @JvmStatic
-    inline val isEnabled
-        get() = Flags.mediaInSceneContainer()
-
-    /**
-     * 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 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/people/NotificationHelper.java b/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java
index d863dcc..710142b6 100644
--- a/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java
@@ -96,7 +96,7 @@
                     if (messages2 == null) {
                         return -1;
                     }
-                    return (int) (n2.when - n1.when);
+                    return (int) (n2.getWhen() - n1.getWhen());
                 }
             };
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 042fb63f..4ee2db7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -75,10 +75,6 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        // set layer to make alpha animation of brightness slider nicer - otherwise elements
-        // of slider are animated separately and it doesn't look good. See b/329244723
-        setLayerType(LAYER_TYPE_HARDWARE, null);
-
         mQSPanelContainer = findViewById(R.id.expanded_qs_scroll_view);
         mQSPanel = findViewById(R.id.quick_settings_panel);
         mHeader = findViewById(R.id.header);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 58858df..829c419 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -332,6 +332,14 @@
         return mTiles.size();
     }
 
+    public int getItemCountForAccessibility() {
+        if (mAccessibilityAction == ACTION_MOVE) {
+            return mEditIndex;
+        } else {
+            return getItemCount();
+        }
+    }
+
     @Override
     public boolean onFailedToRecycleView(Holder holder) {
         holder.stopDrag();
@@ -406,6 +414,10 @@
         } else if (selectable && mAccessibilityAction == ACTION_MOVE) {
             info.state.contentDescription = mContext.getString(
                     R.string.accessibility_qs_edit_tile_move_to_position, position);
+        } else if (!selectable && (mAccessibilityAction == ACTION_MOVE
+                || mAccessibilityAction == ACTION_ADD)) {
+            info.state.contentDescription = mContext.getString(
+                    R.string.accessibilit_qs_edit_tile_add_move_invalid_position);
         } else {
             info.state.contentDescription = info.state.label;
         }
@@ -424,14 +436,15 @@
         holder.mTileView.setOnClickListener(null);
         holder.mTileView.setFocusable(true);
         holder.mTileView.setFocusableInTouchMode(true);
+        holder.mTileView.setAccessibilityTraversalBefore(View.NO_ID);
 
         if (mAccessibilityAction != ACTION_NONE) {
             holder.mTileView.setClickable(selectable);
             holder.mTileView.setFocusable(selectable);
             holder.mTileView.setFocusableInTouchMode(selectable);
-            holder.mTileView.setImportantForAccessibility(selectable
-                    ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
-                    : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+//            holder.mTileView.setImportantForAccessibility(selectable
+//                    ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
+//                    : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
             if (selectable) {
                 holder.mTileView.setOnClickListener(new OnClickListener() {
                     @Override
@@ -911,4 +924,5 @@
         int estimatedTileViewHeight = mTempTextView.getMeasuredHeight() * 2 + padding * 2;
         mMinTileViewHeight = Math.max(minHeight, estimatedTileViewHeight);
     }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
index fdc596b..eec5d3d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
@@ -38,7 +39,9 @@
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
+import com.android.systemui.utils.coroutines.flow.mapLatestConflated
 import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
@@ -48,6 +51,7 @@
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
 
 @OptIn(ExperimentalCoroutinesApi::class)
 /** Observes internet state changes providing the [InternetTileModel]. */
@@ -55,6 +59,7 @@
 @Inject
 constructor(
     private val context: Context,
+    @Main private val mainCoroutineContext: CoroutineContext,
     @Application private val scope: CoroutineScope,
     airplaneModeRepository: AirplaneModeRepository,
     private val connectivityRepository: ConnectivityRepository,
@@ -111,42 +116,48 @@
                 notConnectedFlow
             } else {
                 combine(
-                    it.networkName,
-                    it.signalLevelIcon,
-                    mobileDataContentName,
-                ) { networkNameModel, signalIcon, dataContentDescription ->
-                    when (signalIcon) {
-                        is SignalIconModel.Cellular -> {
-                            val secondary =
-                                mobileDataContentConcat(
-                                    networkNameModel.name,
-                                    dataContentDescription
+                        it.networkName,
+                        it.signalLevelIcon,
+                        mobileDataContentName,
+                    ) { networkNameModel, signalIcon, dataContentDescription ->
+                        Triple(networkNameModel, signalIcon, dataContentDescription)
+                    }
+                    .mapLatestConflated { (networkNameModel, signalIcon, dataContentDescription) ->
+                        when (signalIcon) {
+                            is SignalIconModel.Cellular -> {
+                                val secondary =
+                                    mobileDataContentConcat(
+                                        networkNameModel.name,
+                                        dataContentDescription
+                                    )
+
+                                val drawable =
+                                    withContext(mainCoroutineContext) { SignalDrawable(context) }
+                                drawable.setLevel(signalIcon.level)
+                                val loadedIcon = Icon.Loaded(drawable, null)
+
+                                InternetTileModel.Active(
+                                    secondaryTitle = secondary,
+                                    icon = loadedIcon,
+                                    stateDescription =
+                                        ContentDescription.Loaded(secondary.toString()),
+                                    contentDescription = ContentDescription.Loaded(internetLabel),
                                 )
-
-                            val stateLevel = signalIcon.level
-                            val drawable = SignalDrawable(context)
-                            drawable.setLevel(stateLevel)
-                            val loadedIcon = Icon.Loaded(drawable, null)
-
-                            InternetTileModel.Active(
-                                secondaryTitle = secondary,
-                                icon = loadedIcon,
-                                stateDescription = ContentDescription.Loaded(secondary.toString()),
-                                contentDescription = ContentDescription.Loaded(internetLabel),
-                            )
-                        }
-                        is SignalIconModel.Satellite -> {
-                            val secondary =
-                                signalIcon.icon.contentDescription.loadContentDescription(context)
-                            InternetTileModel.Active(
-                                secondaryTitle = secondary,
-                                iconId = signalIcon.icon.res,
-                                stateDescription = ContentDescription.Loaded(secondary),
-                                contentDescription = ContentDescription.Loaded(internetLabel),
-                            )
+                            }
+                            is SignalIconModel.Satellite -> {
+                                val secondary =
+                                    signalIcon.icon.contentDescription.loadContentDescription(
+                                        context
+                                    )
+                                InternetTileModel.Active(
+                                    secondaryTitle = secondary,
+                                    iconId = signalIcon.icon.res,
+                                    stateDescription = ContentDescription.Loaded(secondary),
+                                    contentDescription = ContentDescription.Loaded(internetLabel),
+                                )
+                            }
                         }
                     }
-                }
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
index 234eda8..cf33c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
@@ -27,8 +27,6 @@
 import com.android.systemui.keyguard.KeyguardWmStateRefactor
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.shared.ComposeLockscreen
-import com.android.systemui.keyguard.shared.RefactorKeyguardDismissIntent
-import com.android.systemui.media.controls.util.MediaInSceneContainerFlag
 import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
 import com.android.systemui.statusbar.phone.PredictiveBackSysUiFlag
 
@@ -44,12 +42,10 @@
             ComposeLockscreen.isEnabled &&
                 KeyguardBottomAreaRefactor.isEnabled &&
                 KeyguardWmStateRefactor.isEnabled &&
-                MediaInSceneContainerFlag.isEnabled &&
                 MigrateClocksToBlueprint.isEnabled &&
                 NotificationsHeadsUpRefactor.isEnabled &&
                 PredictiveBackSysUiFlag.isEnabled &&
-                DeviceEntryUdfpsRefactor.isEnabled &&
-                RefactorKeyguardDismissIntent.isEnabled
+                DeviceEntryUdfpsRefactor.isEnabled
     // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
 
     /** The main aconfig flag. */
@@ -61,12 +57,10 @@
             ComposeLockscreen.token,
             KeyguardBottomAreaRefactor.token,
             KeyguardWmStateRefactor.token,
-            MediaInSceneContainerFlag.token,
             MigrateClocksToBlueprint.token,
             NotificationsHeadsUpRefactor.token,
             PredictiveBackSysUiFlag.token,
             DeviceEntryUdfpsRefactor.token,
-            RefactorKeyguardDismissIntent.token,
             // NOTE: Changes should also be made in isEnabled and @EnableSceneContainer
         )
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
index 7130fa1..5960462 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
@@ -3,15 +3,19 @@
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
 import android.animation.ValueAnimator
-import android.os.UserHandle
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewGroup.MarginLayoutParams
 import android.view.ViewTreeObserver
 import android.view.animation.AccelerateDecelerateInterpolator
 import androidx.constraintlayout.widget.Guideline
+import com.android.systemui.Flags.screenshotPrivateProfileBehaviorFix
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.res.R
+import com.android.systemui.screenshot.message.ProfileMessageController
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 
 /**
  * MessageContainerController controls the display of content in the screenshot message container.
@@ -20,7 +24,9 @@
 @Inject
 constructor(
     private val workProfileMessageController: WorkProfileMessageController,
+    private val profileMessageController: ProfileMessageController,
     private val screenshotDetectionController: ScreenshotDetectionController,
+    @Application private val mainScope: CoroutineScope,
 ) {
     private lateinit var container: ViewGroup
     private lateinit var guideline: Guideline
@@ -42,43 +48,52 @@
         detectionNoticeView.visibility = View.GONE
     }
 
-    // Minimal implementation for use when Flags.SCREENSHOT_METADATA isn't turned on.
-    fun onScreenshotTaken(userHandle: UserHandle) {
-        val workProfileData = workProfileMessageController.onScreenshotTaken(userHandle)
-        if (workProfileData != null) {
-            workProfileFirstRunView.visibility = View.VISIBLE
-            detectionNoticeView.visibility = View.GONE
-
-            workProfileMessageController.populateView(
-                workProfileFirstRunView,
-                workProfileData,
-                this::animateOutMessageContainer
-            )
-            animateInMessageContainer()
-        }
-    }
-
     fun onScreenshotTaken(screenshot: ScreenshotData) {
-        val workProfileData = workProfileMessageController.onScreenshotTaken(screenshot.userHandle)
-        var notifiedApps: List<CharSequence> =
-            screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
+        if (screenshotPrivateProfileBehaviorFix()) {
+            mainScope.launch {
+                val profileData = profileMessageController.onScreenshotTaken(screenshot.userHandle)
+                var notifiedApps: List<CharSequence> =
+                    screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
 
-        // If work profile first run needs to show, bias towards that, otherwise show screenshot
-        // detection notification if needed.
-        if (workProfileData != null) {
-            workProfileFirstRunView.visibility = View.VISIBLE
-            detectionNoticeView.visibility = View.GONE
-            workProfileMessageController.populateView(
-                workProfileFirstRunView,
-                workProfileData,
-                this::animateOutMessageContainer
-            )
-            animateInMessageContainer()
-        } else if (notifiedApps.isNotEmpty()) {
-            detectionNoticeView.visibility = View.VISIBLE
-            workProfileFirstRunView.visibility = View.GONE
-            screenshotDetectionController.populateView(detectionNoticeView, notifiedApps)
-            animateInMessageContainer()
+                // If profile first run needs to show, bias towards that, otherwise show screenshot
+                // detection notification if needed.
+                if (profileData != null) {
+                    workProfileFirstRunView.visibility = View.VISIBLE
+                    detectionNoticeView.visibility = View.GONE
+                    profileMessageController.bindView(workProfileFirstRunView, profileData) {
+                        animateOutMessageContainer()
+                    }
+                    animateInMessageContainer()
+                } else if (notifiedApps.isNotEmpty()) {
+                    detectionNoticeView.visibility = View.VISIBLE
+                    workProfileFirstRunView.visibility = View.GONE
+                    screenshotDetectionController.populateView(detectionNoticeView, notifiedApps)
+                    animateInMessageContainer()
+                }
+            }
+        } else {
+            val workProfileData =
+                workProfileMessageController.onScreenshotTaken(screenshot.userHandle)
+            var notifiedApps: List<CharSequence> =
+                screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
+
+            // If work profile first run needs to show, bias towards that, otherwise show screenshot
+            // detection notification if needed.
+            if (workProfileData != null) {
+                workProfileFirstRunView.visibility = View.VISIBLE
+                detectionNoticeView.visibility = View.GONE
+                workProfileMessageController.populateView(
+                    workProfileFirstRunView,
+                    workProfileData,
+                    this::animateOutMessageContainer
+                )
+                animateInMessageContainer()
+            } else if (notifiedApps.isNotEmpty()) {
+                detectionNoticeView.visibility = View.VISIBLE
+                workProfileFirstRunView.visibility = View.GONE
+                screenshotDetectionController.populateView(detectionNoticeView, notifiedApps)
+                animateInMessageContainer()
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
index c801ca5..b93aedd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
@@ -53,8 +53,9 @@
         if (userManager.isManagedProfile(userHandle.identifier) && !messageAlreadyDismissed()) {
             var badgedIcon: Drawable? = null
             var label: CharSequence? = null
-            val fileManager = fileManagerComponentName()
-                ?: return WorkProfileFirstRunData(defaultFileAppName(), null)
+            val fileManager =
+                fileManagerComponentName()
+                    ?: return WorkProfileFirstRunData(defaultFileAppName(), null)
             try {
                 val info = packageManager.getActivityInfo(fileManager, ComponentInfoFlags.of(0L))
                 val icon = packageManager.getActivityIcon(fileManager)
@@ -103,9 +104,7 @@
         context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
 
     private fun fileManagerComponentName() =
-        ComponentName.unflattenFromString(
-            context.getString(R.string.config_sceenshotWorkProfileFilesApp)
-        )
+        ComponentName.unflattenFromString(context.getString(R.string.config_screenshotFilesApp))
 
     private fun defaultFileAppName() = context.getString(R.string.screenshot_default_files_app_name)
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 9b8d047..8235325 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -38,6 +38,7 @@
 import com.android.systemui.screenshot.TakeScreenshotService;
 import com.android.systemui.screenshot.appclips.AppClipsScreenshotHelperService;
 import com.android.systemui.screenshot.appclips.AppClipsService;
+import com.android.systemui.screenshot.message.MessageModule;
 import com.android.systemui.screenshot.policy.ScreenshotPolicyModule;
 import com.android.systemui.screenshot.proxy.SystemUiProxyModule;
 import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel;
@@ -51,7 +52,7 @@
 /**
  * Defines injectable resources for Screenshots
  */
-@Module(includes = {ScreenshotPolicyModule.class, SystemUiProxyModule.class})
+@Module(includes = {ScreenshotPolicyModule.class, SystemUiProxyModule.class, MessageModule.class})
 public abstract class ScreenshotModule {
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/message/MessageModule.kt b/packages/SystemUI/src/com/android/systemui/screenshot/message/MessageModule.kt
new file mode 100644
index 0000000..9d0f87f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/message/MessageModule.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.screenshot.message
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface MessageModule {
+    @Binds
+    @SysUISingleton
+    fun bindProfileFirstRunResources(
+        impl: ProfileFirstRunFileResourcesImpl
+    ): ProfileFirstRunFileResources
+
+    @Binds
+    @SysUISingleton
+    fun bindPackageLabelIconProvider(impl: PackageLabelIconProviderImpl): PackageLabelIconProvider
+
+    @Binds
+    @SysUISingleton
+    fun bindProfileFirstRunSettings(impl: ProfileFirstRunSettingsImpl): ProfileFirstRunSettings
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/message/PackageLabelIconProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/message/PackageLabelIconProvider.kt
new file mode 100644
index 0000000..fd073ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/message/PackageLabelIconProvider.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.screenshot.message
+
+import android.content.ComponentName
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import javax.inject.Inject
+
+data class LabeledIcon(
+    val label: CharSequence,
+    val badgedIcon: Drawable?,
+)
+
+/** An object that can fetch a label and icon for a given component. */
+interface PackageLabelIconProvider {
+    /**
+     * @return the label and icon for the given component.
+     * @throws PackageManager.NameNotFoundException if the component was not found.
+     */
+    suspend fun getPackageLabelIcon(
+        componentName: ComponentName,
+        userHandle: UserHandle
+    ): LabeledIcon
+}
+
+class PackageLabelIconProviderImpl @Inject constructor(private val packageManager: PackageManager) :
+    PackageLabelIconProvider {
+
+    override suspend fun getPackageLabelIcon(
+        componentName: ComponentName,
+        userHandle: UserHandle
+    ): LabeledIcon {
+        val info =
+            packageManager.getActivityInfo(componentName, PackageManager.ComponentInfoFlags.of(0L))
+        val icon = packageManager.getActivityIcon(componentName)
+        val badgedIcon = packageManager.getUserBadgedIcon(icon, userHandle)
+        val label = info.loadLabel(packageManager)
+        return LabeledIcon(label, badgedIcon)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/message/ProfileFirstRunFileResources.kt b/packages/SystemUI/src/com/android/systemui/screenshot/message/ProfileFirstRunFileResources.kt
new file mode 100644
index 0000000..e58c76d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/message/ProfileFirstRunFileResources.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.screenshot.message
+
+import android.content.ComponentName
+import android.content.Context
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** Provides various configuration resource values for the profile first run flow. */
+interface ProfileFirstRunFileResources {
+    /** @return the ComponentName for the Files app, if available. */
+    fun fileManagerComponentName(): ComponentName?
+
+    /** @return a default LabeledIcon describing the files app */
+    fun defaultFileApp(): LabeledIcon
+}
+
+class ProfileFirstRunFileResourcesImpl @Inject constructor(private val context: Context) :
+    ProfileFirstRunFileResources {
+    override fun fileManagerComponentName() =
+        ComponentName.unflattenFromString(context.getString(R.string.config_screenshotFilesApp))
+
+    override fun defaultFileApp() =
+        LabeledIcon(
+            context.getString(R.string.screenshot_default_files_app_name),
+            badgedIcon = null
+        )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/message/ProfileFirstRunSettings.kt b/packages/SystemUI/src/com/android/systemui/screenshot/message/ProfileFirstRunSettings.kt
new file mode 100644
index 0000000..5ec14a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/message/ProfileFirstRunSettings.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.screenshot.message
+
+import android.content.Context
+import javax.inject.Inject
+
+/**
+ * An interfaces for the settings related to the profile first run experience, storing a bit
+ * indicating whether the user has already dismissed the message for the given profile.
+ */
+interface ProfileFirstRunSettings {
+    /** @return true if the user has already dismissed the first run message for this profile. */
+    fun messageAlreadyDismissed(profileType: ProfileMessageController.FirstRunProfile): Boolean
+    /**
+     * Update storage to reflect the fact that the user has dismissed a first run message for the
+     * given profile.
+     */
+    fun onMessageDismissed(profileType: ProfileMessageController.FirstRunProfile)
+}
+
+class ProfileFirstRunSettingsImpl @Inject constructor(private val context: Context) :
+    ProfileFirstRunSettings {
+
+    override fun messageAlreadyDismissed(
+        profileType: ProfileMessageController.FirstRunProfile
+    ): Boolean {
+        val preferenceKey = preferenceKey(profileType)
+        return sharedPreference().getBoolean(preferenceKey, false)
+    }
+
+    override fun onMessageDismissed(profileType: ProfileMessageController.FirstRunProfile) {
+        val preferenceKey = preferenceKey(profileType)
+        val editor = sharedPreference().edit()
+        editor.putBoolean(preferenceKey, true)
+        editor.apply()
+    }
+
+    private fun preferenceKey(profileType: ProfileMessageController.FirstRunProfile): String {
+        return when (profileType) {
+            ProfileMessageController.FirstRunProfile.WORK -> WORK_PREFERENCE_KEY
+            ProfileMessageController.FirstRunProfile.PRIVATE -> PRIVATE_PREFERENCE_KEY
+        }
+    }
+
+    private fun sharedPreference() =
+        context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
+
+    companion object {
+        const val SHARED_PREFERENCES_NAME = "com.android.systemui.screenshot"
+        const val WORK_PREFERENCE_KEY = "work_profile_first_run"
+        const val PRIVATE_PREFERENCE_KEY = "private_profile_first_run"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/message/ProfileMessageController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/message/ProfileMessageController.kt
new file mode 100644
index 0000000..9212a6a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/message/ProfileMessageController.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.screenshot.message
+
+import android.os.UserHandle
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import com.android.systemui.res.R
+import com.android.systemui.screenshot.data.model.ProfileType
+import com.android.systemui.screenshot.data.repository.ProfileTypeRepository
+import javax.inject.Inject
+
+/**
+ * Handles work profile and private profile first run, determining whether a first run UI should be
+ * shown and populating that UI if needed.
+ */
+class ProfileMessageController
+@Inject
+constructor(
+    private val packageLabelIconProvider: PackageLabelIconProvider,
+    private val fileResources: ProfileFirstRunFileResources,
+    private val firstRunSettings: ProfileFirstRunSettings,
+    private val profileTypes: ProfileTypeRepository,
+) {
+
+    /**
+     * @return a populated ProfileFirstRunData object if a profile first run message should be
+     *   shown, otherwise null.
+     */
+    suspend fun onScreenshotTaken(userHandle: UserHandle?): ProfileFirstRunData? {
+        if (userHandle == null) return null
+        val profileType =
+            when (profileTypes.getProfileType(userHandle.identifier)) {
+                ProfileType.WORK -> FirstRunProfile.WORK
+                ProfileType.PRIVATE -> FirstRunProfile.PRIVATE
+                else -> return null
+            }
+
+        if (firstRunSettings.messageAlreadyDismissed(profileType)) {
+            return null
+        }
+
+        val fileApp =
+            runCatching {
+                    fileResources.fileManagerComponentName()?.let { fileManager ->
+                        packageLabelIconProvider.getPackageLabelIcon(fileManager, userHandle)
+                    }
+                }
+                .getOrNull() ?: fileResources.defaultFileApp()
+
+        return ProfileFirstRunData(fileApp, profileType)
+    }
+
+    /**
+     * Use the provided ProfileFirstRunData to populate the profile first run UI in the given view.
+     */
+    fun bindView(view: ViewGroup, data: ProfileFirstRunData, animateOut: () -> Unit) {
+        if (data.labeledIcon.badgedIcon != null) {
+            // Replace the default icon if one is provided.
+            val imageView = view.requireViewById<ImageView>(R.id.screenshot_message_icon)
+            imageView.setImageDrawable(data.labeledIcon.badgedIcon)
+        }
+        val messageContent = view.requireViewById<TextView>(R.id.screenshot_message_content)
+        messageContent.text =
+            view.context.getString(messageTemplate(data.profileType), data.labeledIcon.label)
+        view.requireViewById<View>(R.id.message_dismiss_button).setOnClickListener {
+            animateOut()
+            firstRunSettings.onMessageDismissed(data.profileType)
+        }
+    }
+
+    private fun messageTemplate(profile: FirstRunProfile): Int {
+        return when (profile) {
+            FirstRunProfile.WORK -> R.string.screenshot_work_profile_notification
+            FirstRunProfile.PRIVATE -> R.string.screenshot_private_profile_notification
+        }
+    }
+
+    data class ProfileFirstRunData(
+        val labeledIcon: LabeledIcon,
+        val profileType: FirstRunProfile,
+    )
+
+    enum class FirstRunProfile {
+        WORK,
+        PRIVATE
+    }
+
+    companion object {
+        const val TAG = "PrivateProfileMessageCtrl"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index e051dab..92006a4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -63,6 +63,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        setLayerType(LAYER_TYPE_HARDWARE, null);
 
         mSlider = requireViewById(R.id.slider);
         mSlider.setAccessibilityLabel(getContentDescription().toString());
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 67211b1..4d4d177 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -244,6 +244,7 @@
 
 import kotlinx.coroutines.CoroutineDispatcher;
 import kotlinx.coroutines.flow.Flow;
+import kotlinx.coroutines.flow.StateFlow;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -3991,7 +3992,12 @@
                 mExpandLatencyTracking = false;
             }
             float maxPanelHeight = getMaxPanelTransitionDistance();
-            if (mHeightAnimator == null) {
+            if (mHeightAnimator == null && !MigrateClocksToBlueprint.isEnabled()) {
+                // MigrateClocksToBlueprint - There is an edge case where swiping up slightly,
+                // and then swiping down will trigger overscroll logic. Even without this flag
+                // enabled, the notifications can then run into UDFPS. At this point it is
+                // safer to remove overscroll for this one case to prevent overlap.
+
                 // Split shade has its own overscroll logic
                 if (isTracking()) {
                     float overExpansionPixels = Math.max(0, h - maxPanelHeight);
@@ -4055,6 +4061,11 @@
     }
 
     @Override
+    public StateFlow<Float> getUdfpsTransitionToFullShadeProgress() {
+        return mShadeRepository.getUdfpsTransitionToFullShadeProgress();
+    }
+
+    @Override
     public Flow<Float> getLegacyPanelExpansion() {
         return  mShadeRepository.getLegacyShadeExpansion();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index 0c41efd..9322d31 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController
 import java.util.function.Consumer
 import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
 
 /** Empty implementation of ShadeViewController for variants with no shade. */
@@ -92,6 +93,7 @@
     override val shadeFoldAnimator = ShadeFoldAnimatorEmptyImpl()
     @Deprecated("Use SceneInteractor.currentScene instead.")
     override val legacyPanelExpansion = flowOf(0f)
+    override val udfpsTransitionToFullShadeProgress = MutableStateFlow(0f)
 }
 
 class ShadeHeadsUpTrackerEmptyImpl : ShadeHeadsUpTracker {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 5c79e1e..b934d63 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -28,7 +28,7 @@
      * Amount qs has expanded, [0-1]. 0 means fully collapsed, 1 means fully expanded. Quick
      * Settings can be expanded without the full shade expansion.
      */
-    val qsExpansion: StateFlow<Float>
+    @Deprecated("Use ShadeInteractor.qsExpansion instead") val qsExpansion: StateFlow<Float>
 
     /** Amount shade has expanded with regard to the UDFPS location */
     val udfpsTransitionToFullShadeProgress: StateFlow<Float>
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractor.kt
index 2611092..987c016 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractor.kt
@@ -15,9 +15,14 @@
  */
 package com.android.systemui.shade.domain.interactor
 
+import kotlinx.coroutines.flow.StateFlow
+
 /** Allows the lockscreen to control the shade. */
 interface ShadeLockscreenInteractor {
 
+    /** Amount shade has expanded with regard to the UDFPS location */
+    val udfpsTransitionToFullShadeProgress: StateFlow<Float>
+
     /**
      * Expand shade so that notifications are visible. Non-split shade: just expanding shade or
      * collapsing QS when they're expanded. Split shade: only expanding shade, notifications are
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
index 318da55..6a8b9ee 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.data.repository.ShadeRepository
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.delay
@@ -32,7 +33,12 @@
     private val shadeInteractor: ShadeInteractor,
     private val sceneInteractor: SceneInteractor,
     private val lockIconViewController: LockIconViewController,
+    shadeRepository: ShadeRepository,
 ) : ShadeLockscreenInteractor {
+
+    override val udfpsTransitionToFullShadeProgress =
+        shadeRepository.udfpsTransitionToFullShadeProgress
+
     override fun expandToNotifications() {
         changeToShadeScene()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index d465973..4d7dacd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -29,6 +29,7 @@
 import android.util.Log;
 
 import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
 import com.android.systemui.media.controls.shared.model.MediaData;
@@ -47,6 +48,7 @@
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.concurrent.Executor;
 
 /**
  * Handles tasks and state related to media notifications. For example, there is a 'current' media
@@ -75,6 +77,8 @@
     private final Context mContext;
     private final ArrayList<MediaListener> mMediaListeners;
 
+    private final Executor mBackgroundExecutor;
+
     protected NotificationPresenter mPresenter;
     private MediaController mMediaController;
     private String mMediaNotificationKey;
@@ -115,13 +119,15 @@
             NotifPipeline notifPipeline,
             NotifCollection notifCollection,
             MediaDataManager mediaDataManager,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            @Background Executor backgroundExecutor) {
         mContext = context;
         mMediaListeners = new ArrayList<>();
         mVisibilityProvider = visibilityProvider;
         mMediaDataManager = mediaDataManager;
         mNotifPipeline = notifPipeline;
         mNotifCollection = notifCollection;
+        mBackgroundExecutor = backgroundExecutor;
 
         setupNotifPipeline();
 
@@ -381,9 +387,11 @@
                 Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
                         + mMediaController.getPackageName());
             }
-            mMediaController.unregisterCallback(mMediaListener);
+            mBackgroundExecutor.execute(() -> {
+                mMediaController.unregisterCallback(mMediaListener);
+                mMediaController = null;
+            });
         }
-        mMediaController = null;
     }
 
     public interface MediaListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 8a53e0c..c17da4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -31,6 +31,7 @@
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dump.DumpHandler;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
@@ -68,6 +69,8 @@
 import dagger.multibindings.ClassKey;
 import dagger.multibindings.IntoMap;
 
+import java.util.concurrent.Executor;
+
 import javax.inject.Provider;
 
 /**
@@ -94,14 +97,16 @@
             NotifPipeline notifPipeline,
             NotifCollection notifCollection,
             MediaDataManager mediaDataManager,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            @Background Executor backgroundExecutor) {
         return new NotificationMediaManager(
                 context,
                 visibilityProvider,
                 notifPipeline,
                 notifCollection,
                 mediaDataManager,
-                dumpManager);
+                dumpManager,
+                backgroundExecutor);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 240ae0c..9c1d073 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -1243,8 +1243,9 @@
         if (cmp != 0) return cmp;
 
         cmp = -1 * Long.compare(
-                o1.getRepresentativeEntry().getSbn().getNotification().when,
-                o2.getRepresentativeEntry().getSbn().getNotification().when);
+                o1.getRepresentativeEntry().getSbn().getNotification().getWhen(),
+                o2.getRepresentativeEntry().getSbn().getNotification().getWhen());
+
         return cmp;
     };
 
@@ -1256,8 +1257,8 @@
         if (cmp != 0) return cmp;
 
         cmp = -1 * Long.compare(
-                o1.getRepresentativeEntry().getSbn().getNotification().when,
-                o2.getRepresentativeEntry().getSbn().getNotification().when);
+                o1.getRepresentativeEntry().getSbn().getNotification().getWhen(),
+                o2.getRepresentativeEntry().getSbn().getNotification().getWhen());
         return cmp;
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt
index 5ce1db2..f253100 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt
@@ -89,7 +89,7 @@
         var futureTime = Long.MAX_VALUE
         groupEntry.children
             .asSequence()
-            .mapNotNull { child -> child.sbn.notification.`when`.takeIf { it > 0 } }
+            .mapNotNull { child -> child.sbn.notification.getWhen().takeIf { it > 0 } }
             .forEach { time ->
                 val isInThePast = currentTimeMillis - time > 0
                 if (isInThePast) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index fb67f7c..f98f77e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -296,7 +296,9 @@
         locationLookupByKey: (String) -> GroupLocation,
     ): NotificationEntry? = postedEntries.asSequence()
         .filter { posted -> !posted.entry.sbn.notification.isGroupSummary }
-        .sortedBy { posted -> -posted.entry.sbn.notification.`when` }
+        .sortedBy { posted ->
+            -posted.entry.sbn.notification.getWhen()
+        }
         .firstOrNull()
         ?.let { posted ->
             posted.entry.takeIf { entry ->
@@ -317,7 +319,7 @@
         .filter { locationLookupByKey(it.key) != GroupLocation.Detached }
         .sortedWith(compareBy(
             { !mPostedEntries.contains(it.key) },
-            { -it.sbn.notification.`when` },
+            { -it.sbn.notification.getWhen() },
         ))
         .firstOrNull()
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index 9619aca..bfc5932 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -153,12 +153,12 @@
         uiEventId = HUN_SUPPRESSED_OLD_WHEN
     ) {
     private fun whenAge(entry: NotificationEntry) =
-        systemClock.currentTimeMillis() - entry.sbn.notification.`when`
+        systemClock.currentTimeMillis() - entry.sbn.notification.getWhen()
 
     override fun shouldSuppress(entry: NotificationEntry): Boolean =
         when {
             // Ignore a "when" of 0, as it is unlikely to be a meaningful timestamp.
-            entry.sbn.notification.`when` <= 0L -> false
+            entry.sbn.notification.getWhen() <= 0L -> false
 
             // Assume all HUNs with FSIs, foreground services, or user-initiated jobs are
             // time-sensitive, regardless of their "when".
@@ -278,7 +278,7 @@
     private fun calculateState(entry: NotificationEntry): State {
         if (
             entry.ranking.isConversation &&
-                entry.sbn.notification.`when` > avalancheProvider.startTime
+                entry.sbn.notification.getWhen() > avalancheProvider.startTime
         ) {
             return State.ALLOW_CONVERSATION_AFTER_AVALANCHE
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index d591114..9c6a423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -623,7 +623,7 @@
             return false;
         }
 
-        final long when = notification.when;
+        final long when = notification.getWhen();
         final long now = mSystemClock.currentTimeMillis();
         final long age = now - when;
 
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 bdeaabf..5e3df7b 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
@@ -2769,7 +2769,7 @@
         }
         if (!mIsSummaryWithChildren && wasSummary) {
             // Reset the 'when' once the row stops being a summary
-            mPublicLayout.setNotificationWhen(mEntry.getSbn().getNotification().when);
+            mPublicLayout.setNotificationWhen(mEntry.getSbn().getNotification().getWhen());
         }
         getShowingLayout().updateBackgroundColor(false /* animate */);
         mPrivateLayout.updateExpandButtons(isExpandable());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 37bbbd0..ac4bd09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AodToOccludedTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
@@ -106,6 +107,7 @@
     private val notificationStackAppearanceInteractor: NotificationStackAppearanceInteractor,
     private val alternateBouncerToGoneTransitionViewModel:
         AlternateBouncerToGoneTransitionViewModel,
+    private val aodToGoneTransitionViewModel: AodToGoneTransitionViewModel,
     private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
     private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel,
     private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
@@ -464,6 +466,7 @@
         val alphaTransitions =
             merge(
                 alternateBouncerToGoneTransitionViewModel.notificationAlpha(viewState),
+                aodToGoneTransitionViewModel.notificationAlpha(viewState),
                 aodToLockscreenTransitionViewModel.notificationAlpha,
                 aodToOccludedTransitionViewModel.lockscreenAlpha(viewState),
                 dozingToLockscreenTransitionViewModel.lockscreenAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index b2b2cea..7630d43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -855,6 +855,7 @@
         mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
         mBubblesOptional.ifPresent(this::initBubbles);
+        mKeyguardBypassController.listenForQsExpandedChange();
 
         mStatusBarSignalPolicy.init();
         mKeyguardIndicationController.init();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index a8941bb..97791ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -21,7 +21,6 @@
 import android.content.res.Resources
 import android.hardware.biometrics.BiometricSourceType
 import android.provider.Settings
-import androidx.annotation.VisibleForTesting
 import com.android.app.tracing.ListenersTracing.forEachTraced
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
@@ -32,7 +31,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
-import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm
@@ -41,23 +40,24 @@
 import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.tuner.TunerService
+import dagger.Lazy
+import java.io.PrintWriter
+import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
-import java.io.PrintWriter
-import javax.inject.Inject
 
 @SysUISingleton
 class KeyguardBypassController @Inject constructor(
         @Main resources: Resources,
         packageManager: PackageManager,
-        @Application applicationScope: CoroutineScope,
+        @Application private val applicationScope: CoroutineScope,
         tunerService: TunerService,
         private val statusBarStateController: StatusBarStateController,
         lockscreenUserManager: NotificationLockscreenUserManager,
         private val keyguardStateController: KeyguardStateController,
-        private val shadeRepository: ShadeRepository,
+        private val shadeInteractorLazy: Lazy<ShadeInteractor>,
         devicePostureController: DevicePostureController,
         private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
         dumpManager: DumpManager
@@ -144,7 +144,6 @@
                     }
                 }
             })
-            listenForQsExpandedChange(applicationScope)
             val dismissByDefault = if (resources.getBoolean(
                             com.android.internal.R.bool.config_faceAuthDismissesKeyguard)) 1 else 0
             tunerService.addTunable({ key, _ ->
@@ -159,10 +158,9 @@
         }
     }
 
-    @VisibleForTesting
-    fun listenForQsExpandedChange(scope: CoroutineScope) =
-        scope.launch {
-            shadeRepository.qsExpansion.map { it > 0f }.distinctUntilChanged()
+    fun listenForQsExpandedChange() =
+        applicationScope.launch {
+            shadeInteractorLazy.get().qsExpansion.map { it > 0f }.distinctUntilChanged()
                 .collect { isQsExpanded ->
                     val changed = qsExpanded != isQsExpanded
                     qsExpanded = isQsExpanded
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index 68d54e7..6b68511 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -131,7 +131,12 @@
 
         val runnable = Runnable {
             assistManagerLazy.get().hideAssist()
-            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
+            intent.flags =
+                if (intent.flags and Intent.FLAG_ACTIVITY_REORDER_TO_FRONT != 0) {
+                    Intent.FLAG_ACTIVITY_NEW_TASK
+                } else {
+                    Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
+                }
             intent.addFlags(flags)
             val result = intArrayOf(ActivityManager.START_CANCELED)
             activityTransitionAnimator.startIntentWithAnimation(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index f35d199..7301b87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -72,7 +72,6 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
-import com.android.systemui.keyguard.shared.RefactorKeyguardDismissIntent;
 import com.android.systemui.keyguard.shared.model.DismissAction;
 import com.android.systemui.keyguard.shared.model.KeyguardDone;
 import com.android.systemui.keyguard.shared.model.KeyguardState;
@@ -800,7 +799,7 @@
 
     public void dismissWithAction(OnDismissAction r, Runnable cancelAction,
             boolean afterKeyguardGone, String message) {
-        if (RefactorKeyguardDismissIntent.isEnabled()) {
+        if (SceneContainerFlag.isEnabled()) {
             if (r == null) {
                 return;
             }
@@ -852,7 +851,7 @@
                     return;
                 }
 
-                if (!RefactorKeyguardDismissIntent.isEnabled()) {
+                if (!SceneContainerFlag.isEnabled()) {
                     mAfterKeyguardGoneAction = r;
                     mKeyguardGoneCancelAction = cancelAction;
                     mDismissActionWillAnimateOnKeyguard = r != null
@@ -920,7 +919,7 @@
      * Adds a {@param runnable} to be executed after Keyguard is gone.
      */
     public void addAfterKeyguardGoneRunnable(Runnable runnable) {
-        if (RefactorKeyguardDismissIntent.isEnabled()) {
+        if (SceneContainerFlag.isEnabled()) {
             if (runnable != null) {
                 mKeyguardDismissActionInteractor.get().runAfterKeyguardGone(runnable);
             }
@@ -1112,7 +1111,7 @@
             // We update the state (which will show the keyguard) only if an animation will run on
             // the keyguard. If there is no animation, we wait before updating the state so that we
             // go directly from bouncer to launcher/app.
-            if (RefactorKeyguardDismissIntent.isEnabled()) {
+            if (SceneContainerFlag.isEnabled()) {
                 if (mKeyguardDismissActionInteractor.get().runDismissAnimationOnKeyguard()) {
                     updateStates();
                 }
@@ -1239,7 +1238,7 @@
     }
 
     private void executeAfterKeyguardGoneAction() {
-        if (RefactorKeyguardDismissIntent.isEnabled()) {
+        if (SceneContainerFlag.isEnabled()) {
             return;
         }
         if (mAfterKeyguardGoneAction != null) {
@@ -1629,8 +1628,8 @@
         pw.println("  isBouncerShowing(): " + isBouncerShowing());
         pw.println("  bouncerIsOrWillBeShowing(): " + primaryBouncerIsOrWillBeShowing());
         pw.println("  Registered KeyguardViewManagerCallbacks:");
-        pw.println(" refactorKeyguardDismissIntent enabled:"
-                + RefactorKeyguardDismissIntent.isEnabled());
+        pw.println(" SceneContainerFlag enabled:"
+                + SceneContainerFlag.isEnabled());
         for (KeyguardViewManagerCallback callback : mCallbacks) {
             pw.println("      " + callback);
         }
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 a20468f..ec88b6c 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
@@ -98,7 +98,7 @@
                     (entry.sbn.key == callNotificationInfo?.key)) {
                 val newOngoingCallInfo = CallNotificationInfo(
                         entry.sbn.key,
-                        entry.sbn.notification.`when`,
+                        entry.sbn.notification.getWhen(),
                         entry.sbn.notification.contentIntent,
                         entry.sbn.uid,
                         entry.sbn.notification.extras.getInt(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index f2255f3..332c121 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -126,10 +126,9 @@
             ) { shouldShow, connectionState ->
                 if (shouldShow) {
                     when (connectionState) {
+                        SatelliteConnectionState.On,
                         SatelliteConnectionState.Connected ->
                             context.getString(R.string.satellite_connected_carrier_text)
-                        SatelliteConnectionState.On ->
-                            context.getString(R.string.satellite_not_connected_carrier_text)
                         SatelliteConnectionState.Off,
                         SatelliteConnectionState.Unknown -> null
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt
index 1e65566..b3ea9dc 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt
@@ -6,8 +6,8 @@
 import android.os.Vibrator
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.updates.FoldProvider
-import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
 import java.util.concurrent.Executor
 import javax.inject.Inject
 
@@ -18,6 +18,7 @@
 constructor(
     unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
     foldProvider: FoldProvider,
+    transitionConfig: UnfoldTransitionConfig,
     @Main private val mainExecutor: Executor,
     private val vibrator: Vibrator?
 ) : TransitionProgressListener {
@@ -27,22 +28,17 @@
             VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK)
 
     init {
-        if (vibrator != null) {
+        if (vibrator != null && transitionConfig.isHapticsEnabled) {
             // We don't need to remove the callback because we should listen to it
             // the whole time when SystemUI process is alive
             unfoldTransitionProgressProvider.addCallback(this)
-        }
 
-        foldProvider.registerCallback(
-            object : FoldCallback {
-                override fun onFoldUpdated(isFolded: Boolean) {
-                    if (isFolded) {
-                        isFirstAnimationAfterUnfold = true
-                    }
+            foldProvider.registerCallback({ isFolded ->
+                if (isFolded) {
+                    isFirstAnimationAfterUnfold = true
                 }
-            },
-            mainExecutor
-        )
+            }, mainExecutor)
+        }
     }
 
     private var lastTransitionProgress = TRANSITION_PROGRESS_FULL_OPEN
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialogReceiver.kt
index f11d5d1..0968bde 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialogReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialogReceiver.kt
@@ -21,26 +21,29 @@
 import android.content.Intent
 import android.provider.Settings
 import android.text.TextUtils
-import android.util.Log
+import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor
+import com.android.systemui.volume.ui.navigation.VolumeNavigator
 import javax.inject.Inject
 
-private const val TAG = "VolumePanelDialogReceiver"
-private const val LAUNCH_ACTION = "com.android.systemui.action.LAUNCH_VOLUME_PANEL_DIALOG"
-private const val DISMISS_ACTION = "com.android.systemui.action.DISMISS_VOLUME_PANEL_DIALOG"
-
-/**
- * BroadcastReceiver for handling volume panel dialog intent
- */
-class VolumePanelDialogReceiver @Inject constructor(
-    private val volumePanelFactory: VolumePanelFactory
+/** [BroadcastReceiver] for handling volume panel dialog intent */
+class VolumePanelDialogReceiver
+@Inject
+constructor(
+    private val volumeNavigator: VolumeNavigator,
+    private val volumePanelNavigationInteractor: VolumePanelNavigationInteractor,
 ) : BroadcastReceiver() {
+
     override fun onReceive(context: Context, intent: Intent) {
-        Log.d(TAG, "onReceive intent" + intent.action)
-        if (TextUtils.equals(LAUNCH_ACTION, intent.action) ||
-                TextUtils.equals(Settings.Panel.ACTION_VOLUME, intent.action)) {
-            volumePanelFactory.create(true, null)
-        } else if (TextUtils.equals(DISMISS_ACTION, intent.action)) {
-            volumePanelFactory.dismiss()
+        if (
+            TextUtils.equals(LAUNCH_ACTION, intent.action) ||
+                TextUtils.equals(Settings.Panel.ACTION_VOLUME, intent.action)
+        ) {
+            volumeNavigator.openVolumePanel(volumePanelNavigationInteractor.getVolumePanelRoute())
         }
     }
+
+    companion object {
+        const val LAUNCH_ACTION = "com.android.systemui.action.LAUNCH_VOLUME_PANEL_DIALOG"
+        const val DISMISS_ACTION = "com.android.systemui.action.DISMISS_VOLUME_PANEL_DIALOG"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index fb92a0f..dc1e8cf 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume.dagger;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.media.AudioManager;
 import android.os.Looper;
@@ -37,6 +38,7 @@
 import com.android.systemui.volume.VolumeComponent;
 import com.android.systemui.volume.VolumeDialogComponent;
 import com.android.systemui.volume.VolumeDialogImpl;
+import com.android.systemui.volume.VolumePanelDialogReceiver;
 import com.android.systemui.volume.VolumeUI;
 import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
 import com.android.systemui.volume.panel.dagger.VolumePanelComponent;
@@ -65,6 +67,15 @@
         }
 )
 public interface VolumeModule {
+
+    /**
+     * Binds [VolumePanelDialogReceiver]
+     */
+    @Binds
+    @IntoMap
+    @ClassKey(VolumePanelDialogReceiver.class)
+    BroadcastReceiver bindVolumePanelDialogReceiver(VolumePanelDialogReceiver receiver);
+
     /** Starts VolumeUI. */
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
index eff408a..a30de1b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
@@ -17,11 +17,14 @@
 package com.android.systemui.volume.panel.ui.viewmodel
 
 import android.content.Context
+import android.content.IntentFilter
 import android.content.res.Resources
+import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.onConfigChanged
+import com.android.systemui.volume.VolumePanelDialogReceiver
 import com.android.systemui.volume.panel.dagger.VolumePanelComponent
 import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
 import com.android.systemui.volume.panel.domain.VolumePanelStartable
@@ -37,6 +40,8 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
@@ -49,6 +54,7 @@
     coroutineScope: CoroutineScope,
     daggerComponentFactory: VolumePanelComponentFactory,
     configurationController: ConfigurationController,
+    broadcastDispatcher: BroadcastDispatcher,
 ) {
 
     private val volumePanelComponent: VolumePanelComponent =
@@ -113,6 +119,10 @@
 
     init {
         volumePanelComponent.volumePanelStartables().onEach(VolumePanelStartable::start)
+        broadcastDispatcher
+            .broadcastFlow(IntentFilter(VolumePanelDialogReceiver.DISMISS_ACTION))
+            .onEach { dismissPanel() }
+            .launchIn(scope)
     }
 
     fun dismissPanel() {
@@ -125,6 +135,7 @@
         @Application private val context: Context,
         private val daggerComponentFactory: VolumePanelComponentFactory,
         private val configurationController: ConfigurationController,
+        private val broadcastDispatcher: BroadcastDispatcher,
     ) {
 
         fun create(coroutineScope: CoroutineScope): VolumePanelViewModel {
@@ -133,6 +144,7 @@
                 coroutineScope,
                 daggerComponentFactory,
                 configurationController,
+                broadcastDispatcher
             )
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
index 4684b80..79e312f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -27,9 +27,11 @@
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction
@@ -229,6 +231,11 @@
             givenOnOccludingApp(true)
             givenFingerprintAllowed(true)
             keyguardRepository.setIsDozing(true)
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.OCCLUDED,
+                to = KeyguardState.DOZING,
+                testScope
+            )
             runCurrent()
 
             // ERROR message
@@ -254,7 +261,7 @@
             assertThat(message).isNull()
         }
 
-    private fun givenOnOccludingApp(isOnOccludingApp: Boolean) {
+    private suspend fun givenOnOccludingApp(isOnOccludingApp: Boolean) {
         powerRepository.setInteractive(true)
         keyguardRepository.setIsDozing(false)
         keyguardRepository.setKeyguardOccluded(isOnOccludingApp)
@@ -262,6 +269,20 @@
         keyguardRepository.setDreaming(false)
         bouncerRepository.setPrimaryShow(!isOnOccludingApp)
         bouncerRepository.setAlternateVisible(!isOnOccludingApp)
+
+        if (isOnOccludingApp) {
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.OCCLUDED,
+                testScope
+            )
+        } else {
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.OCCLUDED,
+                to = KeyguardState.LOCKSCREEN,
+                testScope
+            )
+        }
     }
 
     private fun givenFingerprintAllowed(allowed: Boolean) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 96b7596..35659c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dock.DockManagerFake
+import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
@@ -83,6 +84,7 @@
 )
 @SmallTest
 @RunWith(Parameterized::class)
+@DisableSceneContainer
 class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
new file mode 100644
index 0000000..ef3183a8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.app.admin.DevicePolicyManager
+import android.content.Intent
+import android.os.UserHandle
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dock.DockManagerFake
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
+import com.android.systemui.util.FakeSharedPreferences
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.FakeSettings
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameter
+import org.junit.runners.Parameterized.Parameters
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.ArgumentMatchers.same
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@FlakyTest(
+    bugId = 292574995,
+    detail = "on certain architectures all permutations with startActivity=true is causing failures"
+)
+@SmallTest
+@RunWith(Parameterized::class)
+@EnableSceneContainer
+class KeyguardQuickAffordanceInteractorSceneContainerTest : SysuiTestCase() {
+
+    companion object {
+        private val INTENT = Intent("some.intent.action")
+        private val DRAWABLE =
+            mock<Icon> {
+                whenever(this.contentDescription)
+                    .thenReturn(
+                        ContentDescription.Resource(
+                            res = CONTENT_DESCRIPTION_RESOURCE_ID,
+                        )
+                    )
+            }
+        private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
+
+        @Parameters(
+            name =
+                "needStrongAuthAfterBoot={0}, canShowWhileLocked={1}," +
+                    " keyguardIsUnlocked={2}, needsToUnlockFirst={3}, startActivity={4}"
+        )
+        @JvmStatic
+        fun data() =
+            listOf(
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ false,
+                    /* canShowWhileLocked= */ false,
+                    /* keyguardIsUnlocked= */ false,
+                    /* needsToUnlockFirst= */ true,
+                    /* startActivity= */ false,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ false,
+                    /* canShowWhileLocked= */ false,
+                    /* keyguardIsUnlocked= */ true,
+                    /* needsToUnlockFirst= */ false,
+                    /* startActivity= */ false,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ false,
+                    /* canShowWhileLocked= */ true,
+                    /* keyguardIsUnlocked= */ false,
+                    /* needsToUnlockFirst= */ false,
+                    /* startActivity= */ false,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ false,
+                    /* canShowWhileLocked= */ true,
+                    /* keyguardIsUnlocked= */ true,
+                    /* needsToUnlockFirst= */ false,
+                    /* startActivity= */ false,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ true,
+                    /* canShowWhileLocked= */ false,
+                    /* keyguardIsUnlocked= */ false,
+                    /* needsToUnlockFirst= */ true,
+                    /* startActivity= */ false,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ true,
+                    /* canShowWhileLocked= */ false,
+                    /* keyguardIsUnlocked= */ true,
+                    /* needsToUnlockFirst= */ true,
+                    /* startActivity= */ false,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ true,
+                    /* canShowWhileLocked= */ true,
+                    /* keyguardIsUnlocked= */ false,
+                    /* needsToUnlockFirst= */ true,
+                    /* startActivity= */ false,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ true,
+                    /* canShowWhileLocked= */ true,
+                    /* keyguardIsUnlocked= */ true,
+                    /* needsToUnlockFirst= */ true,
+                    /* startActivity= */ false,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ false,
+                    /* canShowWhileLocked= */ false,
+                    /* keyguardIsUnlocked= */ false,
+                    /* needsToUnlockFirst= */ true,
+                    /* startActivity= */ true,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ false,
+                    /* canShowWhileLocked= */ false,
+                    /* keyguardIsUnlocked= */ true,
+                    /* needsToUnlockFirst= */ false,
+                    /* startActivity= */ true,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ false,
+                    /* canShowWhileLocked= */ true,
+                    /* keyguardIsUnlocked= */ false,
+                    /* needsToUnlockFirst= */ false,
+                    /* startActivity= */ true,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ false,
+                    /* canShowWhileLocked= */ true,
+                    /* keyguardIsUnlocked= */ true,
+                    /* needsToUnlockFirst= */ false,
+                    /* startActivity= */ true,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ true,
+                    /* canShowWhileLocked= */ false,
+                    /* keyguardIsUnlocked= */ false,
+                    /* needsToUnlockFirst= */ true,
+                    /* startActivity= */ true,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ true,
+                    /* canShowWhileLocked= */ false,
+                    /* keyguardIsUnlocked= */ true,
+                    /* needsToUnlockFirst= */ true,
+                    /* startActivity= */ true,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ true,
+                    /* canShowWhileLocked= */ true,
+                    /* keyguardIsUnlocked= */ false,
+                    /* needsToUnlockFirst= */ true,
+                    /* startActivity= */ true,
+                ),
+                arrayOf(
+                    /* needStrongAuthAfterBoot= */ true,
+                    /* canShowWhileLocked= */ true,
+                    /* keyguardIsUnlocked= */ true,
+                    /* needsToUnlockFirst= */ true,
+                    /* startActivity= */ true,
+                ),
+            )
+
+        private val IMMEDIATE = Dispatchers.Main.immediate
+    }
+
+    @Mock private lateinit var lockPatternUtils: LockPatternUtils
+    @Mock private lateinit var keyguardStateController: KeyguardStateController
+    @Mock private lateinit var activityStarter: ActivityStarter
+    @Mock private lateinit var animationController: ActivityTransitionAnimator.Controller
+    @Mock private lateinit var expandable: Expandable
+    @Mock private lateinit var launchAnimator: DialogTransitionAnimator
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
+    @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
+
+    private lateinit var underTest: KeyguardQuickAffordanceInteractor
+    private lateinit var testScope: TestScope
+
+    @JvmField @Parameter(0) var needStrongAuthAfterBoot: Boolean = false
+    @JvmField @Parameter(1) var canShowWhileLocked: Boolean = false
+    @JvmField @Parameter(2) var keyguardIsUnlocked: Boolean = false
+    @JvmField @Parameter(3) var needsToUnlockFirst: Boolean = false
+    @JvmField @Parameter(4) var startActivity: Boolean = false
+    private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig
+    private lateinit var dockManager: DockManagerFake
+    private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+    private lateinit var userTracker: UserTracker
+
+    private val kosmos = testKosmos()
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        whenever(expandable.activityTransitionController()).thenReturn(animationController)
+
+        userTracker = FakeUserTracker()
+        homeControls =
+            FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS)
+        dockManager = DockManagerFake()
+        biometricSettingsRepository = FakeBiometricSettingsRepository()
+        val quickAccessWallet =
+            FakeKeyguardQuickAffordanceConfig(
+                BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
+            )
+        val qrCodeScanner =
+            FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
+        val scope = CoroutineScope(IMMEDIATE)
+        val localUserSelectionManager =
+            KeyguardQuickAffordanceLocalUserSelectionManager(
+                context = context,
+                userFileManager =
+                    mock<UserFileManager>().apply {
+                        whenever(
+                                getSharedPreferences(
+                                    anyString(),
+                                    anyInt(),
+                                    anyInt(),
+                                )
+                            )
+                            .thenReturn(FakeSharedPreferences())
+                    },
+                userTracker = userTracker,
+                broadcastDispatcher = fakeBroadcastDispatcher,
+            )
+        val remoteUserSelectionManager =
+            KeyguardQuickAffordanceRemoteUserSelectionManager(
+                scope = scope,
+                userTracker = userTracker,
+                clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
+                userHandle = UserHandle.SYSTEM,
+            )
+        val quickAffordanceRepository =
+            KeyguardQuickAffordanceRepository(
+                appContext = context,
+                scope = scope,
+                localUserSelectionManager = localUserSelectionManager,
+                remoteUserSelectionManager = remoteUserSelectionManager,
+                userTracker = userTracker,
+                legacySettingSyncer =
+                    KeyguardQuickAffordanceLegacySettingSyncer(
+                        scope = scope,
+                        backgroundDispatcher = IMMEDIATE,
+                        secureSettings = FakeSettings(),
+                        selectionsManager = localUserSelectionManager,
+                    ),
+                configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
+                dumpManager = mock(),
+                userHandle = UserHandle.SYSTEM,
+            )
+        val featureFlags = FakeFeatureFlags()
+        val testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
+        underTest =
+            KeyguardQuickAffordanceInteractor(
+                keyguardInteractor =
+                    KeyguardInteractorFactory.create(
+                            featureFlags = featureFlags,
+                        )
+                        .keyguardInteractor,
+                shadeInteractor = kosmos.shadeInteractor,
+                lockPatternUtils = lockPatternUtils,
+                keyguardStateController = keyguardStateController,
+                userTracker = userTracker,
+                activityStarter = activityStarter,
+                featureFlags = featureFlags,
+                repository = { quickAffordanceRepository },
+                launchAnimator = launchAnimator,
+                logger = logger,
+                devicePolicyManager = devicePolicyManager,
+                dockManager = dockManager,
+                biometricSettingsRepository = biometricSettingsRepository,
+                backgroundDispatcher = testDispatcher,
+                appContext = mContext,
+                sceneInteractor = { kosmos.sceneInteractor },
+            )
+    }
+
+    @Test
+    fun onQuickAffordanceTriggered() =
+        testScope.runTest {
+            val key = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
+            setUpMocks(
+                needStrongAuthAfterBoot = needStrongAuthAfterBoot,
+                keyguardIsUnlocked = keyguardIsUnlocked,
+            )
+
+            homeControls.setState(
+                lockScreenState =
+                    KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                        icon = DRAWABLE,
+                    )
+            )
+            homeControls.onTriggeredResult =
+                if (startActivity) {
+                    KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
+                        intent = INTENT,
+                        canShowWhileLocked = canShowWhileLocked,
+                    )
+                } else {
+                    KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                }
+
+            underTest.onQuickAffordanceTriggered(
+                configKey = "${KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()}::$key",
+                expandable = expandable,
+                slotId = "",
+            )
+
+            if (startActivity) {
+                if (needsToUnlockFirst) {
+                    verify(activityStarter)
+                        .postStartActivityDismissingKeyguard(
+                            any(),
+                            /* delay= */ eq(0),
+                            same(animationController),
+                        )
+                } else {
+                    verify(activityStarter)
+                        .startActivity(
+                            any(),
+                            /* dismissShade= */ eq(true),
+                            same(animationController),
+                            /* showOverLockscreenWhenLocked= */ eq(true),
+                        )
+                }
+            } else {
+                verifyZeroInteractions(activityStarter)
+            }
+        }
+
+    private fun setUpMocks(
+        needStrongAuthAfterBoot: Boolean = true,
+        keyguardIsUnlocked: Boolean = false,
+    ) {
+        whenever(lockPatternUtils.getStrongAuthForUser(any()))
+            .thenReturn(
+                if (needStrongAuthAfterBoot) {
+                    LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
+                } else {
+                    LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
+                }
+            )
+        whenever(keyguardStateController.isUnlocked).thenReturn(keyguardIsUnlocked)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 6b317ea..1881a9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -19,6 +19,7 @@
 import android.app.admin.DevicePolicyManager
 import android.content.Intent
 import android.os.UserHandle
+import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.widget.LockPatternUtils
@@ -32,6 +33,7 @@
 import com.android.systemui.doze.util.BurnInHelperWrapper
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
@@ -75,17 +77,18 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.verifyZeroInteractions
 import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(JUnit4::class)
-class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class KeyguardBottomAreaViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
 
     @Mock private lateinit var expandable: Expandable
     @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
@@ -111,6 +114,10 @@
 
     private val kosmos = testKosmos()
 
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags!!)
+    }
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -750,5 +757,11 @@
     companion object {
         private const val DEFAULT_BURN_IN_OFFSET = 5
         private const val RETURNED_BURN_IN_OFFSET = 3
+
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
index 1d98dc3..0c98cff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
@@ -18,11 +18,12 @@
 
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
 import com.android.systemui.keyguard.data.repository.keyguardClockRepository
 import com.android.systemui.keyguard.data.repository.keyguardRepository
@@ -45,14 +46,14 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(JUnit4::class)
-@DisableSceneContainer
-class KeyguardClockViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class KeyguardClockViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
     val kosmos = testKosmos()
     val testScope = kosmos.testScope
     val underTest = kosmos.keyguardClockViewModel
@@ -65,6 +66,10 @@
     var config = ClockConfig("TEST", "Test", "")
     var faceConfig = ClockFaceConfig()
 
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags!!)
+    }
+
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
@@ -276,5 +281,11 @@
 
     companion object {
         private const val KEYGUARD_STATUS_BAR_HEIGHT = 20
+
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
index 72fc65b..924b872 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
@@ -2,6 +2,8 @@
 
 import android.graphics.drawable.Drawable
 import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.testing.AndroidTestingRunner
 import android.view.View
 import android.view.ViewGroup
@@ -10,10 +12,16 @@
 import androidx.constraintlayout.widget.Guideline
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.screenshot.message.LabeledIcon
+import com.android.systemui.screenshot.message.ProfileMessageController
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import junit.framework.Assert.assertEquals
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -28,6 +36,7 @@
     lateinit var messageContainer: MessageContainerController
 
     @Mock lateinit var workProfileMessageController: WorkProfileMessageController
+    @Mock lateinit var profileMessageController: ProfileMessageController
 
     @Mock lateinit var screenshotDetectionController: ScreenshotDetectionController
 
@@ -46,12 +55,15 @@
     lateinit var workProfileData: WorkProfileMessageController.WorkProfileFirstRunData
 
     @Before
+    @ExperimentalCoroutinesApi
     fun setup() {
         MockitoAnnotations.initMocks(this)
         messageContainer =
             MessageContainerController(
                 workProfileMessageController,
+                profileMessageController,
                 screenshotDetectionController,
+                TestScope(UnconfinedTestDispatcher())
             )
         screenshotView = ConstraintLayout(mContext)
         workProfileData = WorkProfileMessageController.WorkProfileFirstRunData(appName, icon)
@@ -78,20 +90,11 @@
     }
 
     @Test
-    fun testOnScreenshotTakenUserHandle_noWorkProfileFirstRun() {
-        // (just being explicit here)
-        whenever(workProfileMessageController.onScreenshotTaken(eq(userHandle))).thenReturn(null)
-
-        messageContainer.onScreenshotTaken(userHandle)
-
-        verify(workProfileMessageController, never()).populateView(any(), any(), any())
-    }
-
-    @Test
+    @DisableFlags(com.android.systemui.Flags.FLAG_SCREENSHOT_PRIVATE_PROFILE_BEHAVIOR_FIX)
     fun testOnScreenshotTakenUserHandle_withWorkProfileFirstRun() {
         whenever(workProfileMessageController.onScreenshotTaken(eq(userHandle)))
             .thenReturn(workProfileData)
-        messageContainer.onScreenshotTaken(userHandle)
+        messageContainer.onScreenshotTaken(screenshotData)
 
         verify(workProfileMessageController)
             .populateView(eq(workProfileFirstRunView), eq(workProfileData), any())
@@ -100,10 +103,28 @@
     }
 
     @Test
+    @EnableFlags(com.android.systemui.Flags.FLAG_SCREENSHOT_PRIVATE_PROFILE_BEHAVIOR_FIX)
+    fun testOnScreenshotTakenUserHandle_withProfileProfileFirstRun() = runTest {
+        val profileData =
+            ProfileMessageController.ProfileFirstRunData(
+                LabeledIcon(appName, icon),
+                ProfileMessageController.FirstRunProfile.PRIVATE
+            )
+        whenever(profileMessageController.onScreenshotTaken(eq(userHandle))).thenReturn(profileData)
+        messageContainer.onScreenshotTaken(screenshotData)
+
+        verify(profileMessageController)
+            .bindView(eq(workProfileFirstRunView), eq(profileData), any())
+        assertEquals(View.VISIBLE, workProfileFirstRunView.visibility)
+        assertEquals(View.GONE, detectionNoticeView.visibility)
+    }
+
+    @Test
     fun testOnScreenshotTakenScreenshotData_nothingToShow() {
         messageContainer.onScreenshotTaken(screenshotData)
 
         verify(workProfileMessageController, never()).populateView(any(), any(), any())
+        verify(profileMessageController, never()).bindView(any(), any(), any())
         verify(screenshotDetectionController, never()).populateView(any(), any())
 
         assertEquals(View.GONE, container.visibility)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
index 4b7d5f0..ebdc49f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
@@ -87,7 +87,7 @@
         when(mMockContext.getSharedPreferences(
                 eq(WorkProfileMessageController.SHARED_PREFERENCES_NAME),
                 eq(Context.MODE_PRIVATE))).thenReturn(mSharedPreferences);
-        when(mMockContext.getString(R.string.config_sceenshotWorkProfileFilesApp))
+        when(mMockContext.getString(R.string.config_screenshotFilesApp))
                 .thenReturn(FILES_APP_COMPONENT);
         when(mMockContext.getString(R.string.screenshot_default_files_app_name))
                 .thenReturn(DEFAULT_FILES_APP_LABEL);
@@ -149,7 +149,7 @@
 
     @Test
     public void testOnScreenshotTaken_noFilesAppComponentDefined() {
-        when(mMockContext.getString(R.string.config_sceenshotWorkProfileFilesApp))
+        when(mMockContext.getString(R.string.config_screenshotFilesApp))
                 .thenReturn("");
 
         WorkProfileMessageController.WorkProfileFirstRunData data =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/message/ProfileMessageControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/message/ProfileMessageControllerTest.kt
new file mode 100644
index 0000000..ce2facd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/message/ProfileMessageControllerTest.kt
@@ -0,0 +1,181 @@
+/*
+ * 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.screenshot.message
+
+import android.content.ComponentName
+import android.content.pm.PackageManager
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import com.android.systemui.screenshot.data.model.ProfileType
+import com.android.systemui.screenshot.data.repository.ProfileTypeRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+class ProfileMessageControllerTest {
+    @Test
+    fun personalScreenshot() = runTest {
+        assertThat(
+                getMessageController()
+                    .onScreenshotTaken(UserHandle.of(profileTypeRepository.personalUser))
+            )
+            .isNull()
+    }
+
+    @Test
+    fun communalScreenshot() = runTest {
+        assertThat(
+                getMessageController()
+                    .onScreenshotTaken(UserHandle.of(profileTypeRepository.communalUser))
+            )
+            .isNull()
+    }
+
+    @Test
+    fun noUserScreenshot() = runTest {
+        assertThat(getMessageController().onScreenshotTaken(null)).isNull()
+    }
+
+    @Test
+    fun alreadyDismissed() = runTest {
+        val messageController = getMessageController()
+        profileFirstRunSettings.onMessageDismissed(ProfileMessageController.FirstRunProfile.WORK)
+        assertThat(
+                messageController.onScreenshotTaken(UserHandle.of(profileTypeRepository.workUser))
+            )
+            .isNull()
+    }
+
+    @Test
+    fun noFileManager() = runTest {
+        val messageController = getMessageController(fileManagerComponent = null)
+        val data =
+            messageController.onScreenshotTaken(UserHandle.of(profileTypeRepository.workUser))
+        assertThat(data?.profileType).isEqualTo(ProfileMessageController.FirstRunProfile.WORK)
+        assertThat(data?.labeledIcon?.label).isEqualTo(DEFAULT_APP_NAME)
+        assertThat(data?.labeledIcon?.badgedIcon).isNull()
+    }
+
+    @Test
+    fun fileManagerNotFound() = runTest {
+        val messageController =
+            getMessageController(fileManagerComponent = ComponentName("Something", "Random"))
+        val data =
+            messageController.onScreenshotTaken(UserHandle.of(profileTypeRepository.privateUser))
+        assertThat(data?.profileType).isEqualTo(ProfileMessageController.FirstRunProfile.PRIVATE)
+        assertThat(data?.labeledIcon?.label).isEqualTo(DEFAULT_APP_NAME)
+        assertThat(data?.labeledIcon?.badgedIcon).isNull()
+    }
+
+    @Test
+    fun fileManagerFound() = runTest {
+        val messageController = getMessageController()
+        val data =
+            messageController.onScreenshotTaken(UserHandle.of(profileTypeRepository.privateUser))
+        assertThat(data?.profileType).isEqualTo(ProfileMessageController.FirstRunProfile.PRIVATE)
+        assertThat(data?.labeledIcon?.label).isEqualTo(FILE_MANAGER_LABEL)
+        assertThat(data?.labeledIcon?.badgedIcon).isEqualTo(drawable)
+    }
+
+    private val drawable =
+        object : Drawable() {
+            override fun draw(canvas: Canvas) {}
+
+            override fun setAlpha(alpha: Int) {}
+
+            override fun setColorFilter(colorFilter: ColorFilter?) {}
+
+            override fun getOpacity(): Int = 0
+        }
+
+    private val packageLabelIconProvider =
+        object : PackageLabelIconProvider {
+            override suspend fun getPackageLabelIcon(
+                componentName: ComponentName,
+                userHandle: UserHandle
+            ): LabeledIcon {
+                if (componentName.equals(FILE_MANAGER_COMPONENT)) {
+                    return LabeledIcon(FILE_MANAGER_LABEL, drawable)
+                } else {
+                    throw PackageManager.NameNotFoundException()
+                }
+            }
+        }
+
+    private class FakeProfileFirstRunResources(private val fileManager: ComponentName?) :
+        ProfileFirstRunFileResources {
+        override fun fileManagerComponentName(): ComponentName? {
+            return fileManager
+        }
+
+        override fun defaultFileApp() = LabeledIcon(DEFAULT_APP_NAME, badgedIcon = null)
+    }
+
+    private val profileFirstRunSettings =
+        object : ProfileFirstRunSettings {
+            private val dismissed =
+                mutableMapOf(
+                    ProfileMessageController.FirstRunProfile.WORK to false,
+                    ProfileMessageController.FirstRunProfile.PRIVATE to false,
+                )
+
+            override fun messageAlreadyDismissed(
+                profileType: ProfileMessageController.FirstRunProfile
+            ): Boolean {
+                return dismissed.getOrDefault(profileType, false)
+            }
+
+            override fun onMessageDismissed(profileType: ProfileMessageController.FirstRunProfile) {
+                dismissed[profileType] = true
+            }
+        }
+
+    private val profileTypeRepository =
+        object : ProfileTypeRepository {
+            override suspend fun getProfileType(userId: Int): ProfileType {
+                return when (userId) {
+                    workUser -> ProfileType.WORK
+                    privateUser -> ProfileType.PRIVATE
+                    communalUser -> ProfileType.COMMUNAL
+                    else -> ProfileType.NONE
+                }
+            }
+
+            val personalUser = 0
+            val workUser = 1
+            val privateUser = 2
+            val communalUser = 3
+        }
+
+    private fun getMessageController(
+        fileManagerComponent: ComponentName? = FILE_MANAGER_COMPONENT
+    ) =
+        ProfileMessageController(
+            packageLabelIconProvider,
+            FakeProfileFirstRunResources(fileManagerComponent),
+            profileFirstRunSettings,
+            profileTypeRepository
+        )
+
+    companion object {
+        val FILE_MANAGER_COMPONENT = ComponentName("package", "component")
+        const val DEFAULT_APP_NAME = "default app"
+        const val FILE_MANAGER_LABEL = "file manager"
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 0a9bac9..7ade053 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -49,6 +49,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.Flags;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.PendingIntent;
@@ -58,6 +59,7 @@
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
 import android.testing.AndroidTestingRunner;
 
 import androidx.test.filters.SmallTest;
@@ -469,6 +471,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
     public void testShouldHeadsUp_oldWhen_whenZero() throws Exception {
         ensureStateForHeadsUpWhenAwake();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
index 4dd97bc..9b4f931 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
@@ -17,19 +17,21 @@
 package com.android.systemui.statusbar.phone
 
 import android.content.pm.PackageManager
-import android.testing.AndroidTestingRunner
+import android.platform.test.flag.junit.FlagsParameterization
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
-import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.policy.DevicePostureController
 import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED
@@ -58,15 +60,17 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @TestableLooper.RunWithLooper
-class KeyguardBypassControllerTest : SysuiTestCase() {
+class KeyguardBypassControllerTest(flags: FlagsParameterization?) : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val featureFlags = FakeFeatureFlags()
-    private val shadeRepository = FakeShadeRepository()
+    private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
 
     private lateinit var keyguardBypassController: KeyguardBypassController
     private lateinit var postureControllerCallback: DevicePostureController.Callback
@@ -79,6 +83,18 @@
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var packageManager: PackageManager
 
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags!!)
+    }
+
     @Captor
     private val postureCallbackCaptor =
         ArgumentCaptor.forClass(DevicePostureController.Callback::class.java)
@@ -148,7 +164,7 @@
                 statusBarStateController,
                 lockscreenUserManager,
                 keyguardStateController,
-                shadeRepository,
+                { kosmos.shadeInteractor },
                 devicePostureController,
                 keyguardTransitionInteractor,
                 dumpManager,
@@ -293,13 +309,13 @@
         testScope.runTest {
             initKeyguardBypassController()
             assertThat(keyguardBypassController.qsExpanded).isFalse()
-            val job = keyguardBypassController.listenForQsExpandedChange(this)
-            shadeRepository.setQsExpansion(0.5f)
+            val job = keyguardBypassController.listenForQsExpandedChange()
+            shadeTestUtil.setQsExpansion(0.5f)
             runCurrent()
 
             assertThat(keyguardBypassController.qsExpanded).isTrue()
 
-            shadeRepository.setQsExpansion(0f)
+            shadeTestUtil.setQsExpansion(0f)
             runCurrent()
 
             assertThat(keyguardBypassController.qsExpanded).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index d365663..ba38f87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -314,6 +314,7 @@
     private class UnfoldConfig : UnfoldTransitionConfig {
         override var isEnabled: Boolean = false
         override var isHingeAngleEnabled: Boolean = false
+        override val isHapticsEnabled: Boolean = false
         override val halfFoldedTimeoutMillis: Int = 0
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index efd7f99..05464f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -23,6 +23,7 @@
 import android.app.PendingIntent
 import android.app.Person
 import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.service.notification.NotificationListenerService.REASON_USER_STOPPED
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
@@ -194,7 +195,7 @@
 
     /** Regression test for b/192379214. */
     @Test
-    @DisableFlags(android.app.Flags.FLAG_UPDATE_RANKING_TIME)
+    @DisableFlags(android.app.Flags.FLAG_SORT_SECTION_BY_TIME)
     fun onEntryUpdated_notificationWhenIsZero_timeHidden() {
         val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
         notification.modifyNotification(context).setWhen(0)
@@ -210,6 +211,22 @@
     }
 
     @Test
+    @EnableFlags(android.app.Flags.FLAG_SORT_SECTION_BY_TIME)
+    fun onEntryUpdated_notificationWhenIsZero_timeShown() {
+        val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
+        notification.modifyNotification(context).setWhen(0)
+
+        notifCollectionListener.onEntryUpdated(notification.build())
+        chipView.measure(
+                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+        )
+
+        assertThat(chipView.findViewById<View>(R.id.ongoing_call_chip_time)?.measuredWidth)
+                .isGreaterThan(0)
+    }
+
+    @Test
     fun onEntryUpdated_notificationWhenIsValid_timeShown() {
         val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
         notification.modifyNotification(context).setWhen(clock.currentTimeMillis())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index 8eea29b..ceaae9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -587,7 +587,7 @@
             advanceTimeBy(10.seconds)
 
             assertThat(latest)
-                .isEqualTo(context.getString(R.string.satellite_not_connected_carrier_text))
+                .isEqualTo(context.getString(R.string.satellite_connected_carrier_text))
         }
 
     @OptIn(ExperimentalCoroutinesApi::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
index bdd3d18..cfa734a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
@@ -16,71 +16,69 @@
 
 package com.android.systemui.statusbar.ui.viewmodel
 
+import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.fromGoneTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.flags.andSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.power.domain.interactor.PowerInteractorFactory
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.statusbar.data.repository.FakeKeyguardStatusBarRepository
-import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor
-import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardStatusBarInteractor
 import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.batteryController
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
+import org.junit.runner.RunWith
 import org.mockito.Mockito.verify
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
-class KeyguardStatusBarViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class KeyguardStatusBarViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val keyguardRepository = FakeKeyguardRepository()
-    private val keyguardInteractor =
-        KeyguardInteractor(
-            keyguardRepository,
-            mock<CommandQueue>(),
-            PowerInteractorFactory.create().powerInteractor,
-            FakeKeyguardBouncerRepository(),
-            ConfigurationInteractor(FakeConfigurationRepository()),
-            FakeShadeRepository(),
-            kosmos.keyguardTransitionInteractor,
-            { kosmos.sceneInteractor },
-            { kosmos.fromGoneTransitionInteractor },
-            { kosmos.sharedNotificationContainerInteractor },
-            testScope,
-        )
-    private val keyguardStatusBarInteractor =
-        KeyguardStatusBarInteractor(
-            FakeKeyguardStatusBarRepository(),
-        )
-    private val batteryController = mock<BatteryController>()
+    private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
+    private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
+    private val keyguardStatusBarInteractor by lazy { kosmos.keyguardStatusBarInteractor }
+    private val batteryController = kosmos.batteryController
 
-    private val underTest =
-        KeyguardStatusBarViewModel(
-            testScope.backgroundScope,
-            keyguardInteractor,
-            keyguardStatusBarInteractor,
-            batteryController,
-        )
+    lateinit var underTest: KeyguardStatusBarViewModel
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags!!)
+    }
+
+    @Before
+    fun setup() {
+        underTest =
+            KeyguardStatusBarViewModel(
+                testScope.backgroundScope,
+                keyguardInteractor,
+                keyguardStatusBarInteractor,
+                batteryController,
+            )
+    }
 
     @Test
     fun isVisible_dozing_false() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
index fd513c9..06f1a88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
@@ -17,13 +17,12 @@
 
 import android.os.VibrationAttributes
 import android.os.VibrationEffect
-import android.os.Vibrator
+import android.os.vibrator
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.util.TestFoldProvider
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -35,15 +34,20 @@
 @SmallTest
 class UnfoldHapticsPlayerTest : SysuiTestCase() {
 
-    private val progressProvider = FakeUnfoldTransitionProvider()
-    private val vibrator: Vibrator = mock()
-    private val testFoldProvider = TestFoldProvider()
+    private val kosmos = testKosmos()
+
+    private val progressProvider = kosmos.fakeUnfoldTransitionProgressProvider
+    private val vibrator = kosmos.vibrator
+    private val transitionConfig = kosmos.unfoldTransitionConfig
+    private val testFoldProvider = kosmos.foldProvider
 
     private lateinit var player: UnfoldHapticsPlayer
 
     @Before
     fun before() {
-        player = UnfoldHapticsPlayer(progressProvider, testFoldProvider, Runnable::run, vibrator)
+        transitionConfig.isHapticsEnabled = true
+        player = UnfoldHapticsPlayer(progressProvider, testFoldProvider, transitionConfig,
+            Runnable::run, vibrator)
     }
 
     @Test
@@ -58,6 +62,21 @@
     }
 
     @Test
+    fun testHapticsDisabled_unfoldingTransitionFinishing_doesNotPlayHaptics() {
+        transitionConfig.isHapticsEnabled = false
+        player = UnfoldHapticsPlayer(progressProvider, testFoldProvider, transitionConfig,
+                Runnable::run, vibrator)
+
+        testFoldProvider.onFoldUpdate(isFolded = true)
+        testFoldProvider.onFoldUpdate(isFolded = false)
+        progressProvider.onTransitionStarted()
+        progressProvider.onTransitionProgress(0.5f)
+        progressProvider.onTransitionFinishing()
+
+        verify(vibrator).vibrate(any<VibrationEffect>(), any<VibrationAttributes>())
+    }
+
+    @Test
     fun testUnfoldingTransitionFinishingLate_doesNotPlayHaptics() {
         testFoldProvider.onFoldUpdate(isFolded = true)
         testFoldProvider.onFoldUpdate(isFolded = false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTestUtilsKosmos.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTestUtilsKosmos.kt
new file mode 100644
index 0000000..c073903
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTestUtilsKosmos.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.unfold
+
+import com.android.systemui.unfold.util.TestFoldProvider
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.unfold.config.TestUnfoldTransitionConfig
+
+var Kosmos.foldProvider: TestFoldProvider by Kosmos.Fixture { TestFoldProvider() }
+
+var Kosmos.unfoldTransitionConfig: TestUnfoldTransitionConfig
+    by Kosmos.Fixture { TestUnfoldTransitionConfig() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
index ab450e2..3c53997 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
@@ -47,6 +47,12 @@
     }
 
     @Test
+    fun testHapticsEnabled() {
+        assertThat(config.isHapticsEnabled).isEqualTo(mContext.resources
+            .getBoolean(com.android.internal.R.bool.config_unfoldTransitionHapticsEnabled))
+    }
+
+    @Test
     fun testHalfFoldedTimeout() {
         assertThat(config.halfFoldedTimeoutMillis).isEqualTo(mContext.resources
             .getInteger(com.android.internal.R.integer.config_unfoldTransitionHalfFoldedTimeout))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/config/TestUnfoldTransitionConfig.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/config/TestUnfoldTransitionConfig.kt
new file mode 100644
index 0000000..05861150
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/config/TestUnfoldTransitionConfig.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.unfold.config
+
+class TestUnfoldTransitionConfig(
+    override var isEnabled: Boolean = false,
+    override var isHingeAngleEnabled: Boolean = false,
+    override var isHapticsEnabled: Boolean = false,
+    override var halfFoldedTimeoutMillis: Int = 1000
+) : UnfoldTransitionConfig
diff --git a/packages/SystemUI/tests/utils/src/android/os/VibratorKosmos.kt b/packages/SystemUI/tests/utils/src/android/os/VibratorKosmos.kt
new file mode 100644
index 0000000..872b25c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/os/VibratorKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * 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 android.os
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.vibrator by Kosmos.Fixture { mock<Vibrator>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
index e83205c5..1107971 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
@@ -21,11 +21,9 @@
 import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
 import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
 import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
-import com.android.systemui.Flags.FLAG_MEDIA_IN_SCENE_CONTAINER
 import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
 import com.android.systemui.Flags.FLAG_NOTIFICATIONS_HEADS_UP_REFACTOR
 import com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_SYSUI
-import com.android.systemui.Flags.FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT
 import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
 
 /**
@@ -36,13 +34,11 @@
     FLAG_COMPOSE_LOCKSCREEN,
     FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
     FLAG_KEYGUARD_WM_STATE_REFACTOR,
-    FLAG_MEDIA_IN_SCENE_CONTAINER,
     FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
     FLAG_NOTIFICATIONS_HEADS_UP_REFACTOR,
     FLAG_PREDICTIVE_BACK_SYSUI,
     FLAG_SCENE_CONTAINER,
     FLAG_DEVICE_ENTRY_UDFPS_REFACTOR,
-    FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT,
 )
 @Retention(AnnotationRetention.RUNTIME)
 @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
index 4221d06..297d1d8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.shade.data.repository.shadeRepository
 import com.android.systemui.util.mockito.mock
 
 val Kosmos.shadeLockscreenInteractor by
@@ -28,5 +29,6 @@
             shadeInteractor = shadeInteractorImpl,
             sceneInteractor = sceneInteractor,
             lockIconViewController = mock(),
+            shadeRepository = shadeRepository,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryKosmos.kt
new file mode 100644
index 0000000..da95ee9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryKosmos.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.systemui.statusbar.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.keyguardStatusBarRepository: FakeKeyguardStatusBarRepository by
+    Kosmos.Fixture { fakeKeyguardStatusBarRepository }
+
+val Kosmos.fakeKeyguardStatusBarRepository: FakeKeyguardStatusBarRepository by
+    Kosmos.Fixture { FakeKeyguardStatusBarRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardStatusBarInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardStatusBarInteractorKosmos.kt
new file mode 100644
index 0000000..71ed5f6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardStatusBarInteractorKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.statusbar.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.data.repository.keyguardStatusBarRepository
+
+val Kosmos.keyguardStatusBarInteractor: KeyguardStatusBarInteractor by
+    Kosmos.Fixture {
+        KeyguardStatusBarInteractor(
+            keyguardStatusBarRepository,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index cbba80b..d00eedf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.viewmodel.alternateBouncerToGoneTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel
+import com.android.systemui.keyguard.ui.viewmodel.aodToGoneTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.aodToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.aodToOccludedTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.dozingToLockscreenTransitionViewModel
@@ -60,6 +61,7 @@
         shadeInteractor = shadeInteractor,
         notificationStackAppearanceInteractor = notificationStackAppearanceInteractor,
         alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel,
+        aodToGoneTransitionViewModel = aodToGoneTransitionViewModel,
         aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
         aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel,
         dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt
index 4a79452..146f109 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.volume.panel
 
 import android.content.res.mainResources
+import com.android.systemui.broadcast.broadcastDispatcher
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.policy.fakeConfigurationController
@@ -72,5 +73,6 @@
             testScope.backgroundScope,
             KosmosVolumePanelComponentFactory(this),
             fakeConfigurationController,
+            broadcastDispatcher,
         )
     }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
index c513729..ca1daf6 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
@@ -33,6 +33,12 @@
         Resources.getSystem().getBoolean(id)
     }
 
+    override val isHapticsEnabled: Boolean by lazy {
+        val id = Resources.getSystem()
+            .getIdentifier("config_unfoldTransitionHapticsEnabled", "bool", "android")
+        Resources.getSystem().getBoolean(id)
+    }
+
     override val halfFoldedTimeoutMillis: Int by lazy {
         val id = Resources.getSystem()
             .getIdentifier("config_unfoldTransitionHalfFoldedTimeout", "integer", "android")
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
index 765e862..1084cb3 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
@@ -18,5 +18,6 @@
 interface UnfoldTransitionConfig {
     val isEnabled: Boolean
     val isHingeAngleEnabled: Boolean
+    val isHapticsEnabled: Boolean
     val halfFoldedTimeoutMillis: Int
 }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 300b147..8ee560b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -256,6 +256,7 @@
         "stats_flags_lib",
         "core_os_flags_lib",
         "connectivity_flags_lib",
+        "dreams_flags_lib",
     ],
     javac_shard_size: 50,
     javacflags: [
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c47e42d..1b59c18 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5034,7 +5034,7 @@
     }
 
     @Override
-    public final void finishAttachApplication(long startSeq) {
+    public final void finishAttachApplication(long startSeq, long timestampApplicationOnCreateNs) {
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
 
@@ -5054,6 +5054,11 @@
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
+
+        if (android.app.Flags.appStartInfoTimestamps() && timestampApplicationOnCreateNs > 0) {
+            addStartInfoTimestampInternal(ApplicationStartInfo.START_TIMESTAMP_APPLICATION_ONCREATE,
+                    timestampApplicationOnCreateNs, UserHandle.getUserId(uid), uid);
+        }
     }
 
     private void handleBindApplicationTimeoutSoft(ProcessRecord app, int softTimeoutMillis) {
@@ -10253,10 +10258,15 @@
         mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId, true,
                 ALLOW_NON_FULL, "addStartInfoTimestamp", null);
 
-        final String packageName = Settings.getPackageNameForUid(mContext, callingUid);
+        addStartInfoTimestampInternal(key, timestampNs, userId, callingUid);
+    }
 
-        mProcessList.getAppStartInfoTracker().addTimestampToStart(packageName,
-                UserHandle.getUid(userId, UserHandle.getAppId(callingUid)), timestampNs, key);
+    private void addStartInfoTimestampInternal(int key, long timestampNs, int userId, int uid) {
+        mProcessList.getAppStartInfoTracker().addTimestampToStart(
+                Settings.getPackageNameForUid(mContext, uid),
+                UserHandle.getUid(userId, UserHandle.getAppId(uid)),
+                timestampNs,
+                key);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 8b64538..9b83ede 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -35,6 +35,10 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
 
 /**
  * Maps system settings to system properties.
@@ -320,15 +324,30 @@
             NAMESPACE_REBOOT_STAGING,
             AsyncTask.THREAD_POOL_EXECUTOR,
             (DeviceConfig.Properties properties) -> {
-              String scope = properties.getNamespace();
-              for (String key : properties.getKeyset()) {
-                String aconfigPropertyName = makeAconfigFlagStagedPropertyName(key);
-                if (aconfigPropertyName == null) {
-                    log("unable to construct system property for " + scope + "/" + key);
-                    return;
+
+              HashMap<String, HashMap<String, String>> propsToStage =
+                  getStagedFlagsWithValueChange(properties);
+
+              for (HashMap.Entry<String, HashMap<String, String>> entry : propsToStage.entrySet()) {
+                String actualNamespace = entry.getKey();
+                HashMap<String, String> flagValuesToStage = entry.getValue();
+
+                for (String flagName : flagValuesToStage.keySet()) {
+                  String stagedValue = flagValuesToStage.get(flagName);
+                  String propertyName = "next_boot." + makeAconfigFlagPropertyName(
+                      actualNamespace, flagName);
+
+                  if (!propertyName.matches(SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX)
+                      || propertyName.contains(SYSTEM_PROPERTY_INVALID_SUBSTRING)) {
+                    log("unable to construct system property for " + actualNamespace
+                        + "/" + flagName);
+                    continue;
+                  }
+
+                  setProperty(propertyName, stagedValue);
                 }
-                setProperty(aconfigPropertyName, properties.getString(key, null));
               }
+
             });
     }
 
@@ -401,35 +420,6 @@
     }
 
     /**
-     * system property name constructing rule for staged aconfig flags, the flag name
-     * is in the form of [namespace]*[actual flag name], we should push the following
-     * to system properties
-     * "next_boot.[actual sys prop name]".
-     * If the name contains invalid characters or substrings for system property name,
-     * will return null.
-     * @param flagName
-     * @return
-     */
-    @VisibleForTesting
-    static String makeAconfigFlagStagedPropertyName(String flagName) {
-        int idx = flagName.indexOf(NAMESPACE_REBOOT_STAGING_DELIMITER);
-        if (idx == -1 || idx == flagName.length() - 1 || idx == 0) {
-            log("invalid staged flag: " + flagName);
-            return null;
-        }
-
-        String propertyName = "next_boot." + makeAconfigFlagPropertyName(
-                flagName.substring(0, idx), flagName.substring(idx+1));
-
-        if (!propertyName.matches(SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX)
-                || propertyName.contains(SYSTEM_PROPERTY_INVALID_SUBSTRING)) {
-            return null;
-        }
-
-        return propertyName;
-    }
-
-    /**
      * system property name constructing rule for aconfig flags:
      * "persist.device_config.aconfig_flags.[category_name].[flag_name]".
      * If the name contains invalid characters or substrings for system property name,
@@ -451,6 +441,63 @@
         return propertyName;
     }
 
+    /**
+     * Get the flags that need to be staged in sys prop, only these with a real value
+     * change needs to be staged in sys prop. Otherwise, the flag stage is useless and
+     * create performance problem at sys prop side.
+     * @param properties
+     * @return a hash map of namespace name to actual flags to stage
+     */
+    @VisibleForTesting
+    static HashMap<String, HashMap<String, String>> getStagedFlagsWithValueChange(
+        DeviceConfig.Properties properties) {
+
+      // sort flags by actual namespace of the flag
+      HashMap<String, HashMap<String, String>> stagedProps = new HashMap<>();
+      for (String flagName : properties.getKeyset()) {
+        int idx = flagName.indexOf(NAMESPACE_REBOOT_STAGING_DELIMITER);
+        if (idx == -1 || idx == flagName.length() - 1 || idx == 0) {
+          log("invalid staged flag: " + flagName);
+          continue;
+        }
+        String actualNamespace = flagName.substring(0, idx);
+        String actualFlagName = flagName.substring(idx+1);
+        HashMap<String, String> flagStagedValues = stagedProps.get(actualNamespace);
+        if (flagStagedValues == null) {
+          flagStagedValues = new HashMap<String, String>();
+          stagedProps.put(actualNamespace, flagStagedValues);
+        }
+        flagStagedValues.put(actualFlagName, properties.getString(flagName, null));
+      }
+
+      // for each namespace, find flags with real flag value change
+      HashMap<String, HashMap<String, String>> propsToStage = new HashMap<>();
+      for (HashMap.Entry<String, HashMap<String, String>> entry : stagedProps.entrySet()) {
+        String actualNamespace = entry.getKey();
+        HashMap<String, String> flagStagedValues = entry.getValue();
+        Map<String, String> flagCurrentValues = Settings.Config.getStrings(
+            actualNamespace, new ArrayList<String>(flagStagedValues.keySet()));
+
+        HashMap<String, String> flagsToStage = new HashMap<>();
+        for (String flagName : flagStagedValues.keySet()) {
+          String stagedValue = flagStagedValues.get(flagName);
+          String currentValue = flagCurrentValues.get(flagName);
+          if (currentValue == null) {
+            currentValue = new String("false");
+          }
+          if (stagedValue != null && !stagedValue.equalsIgnoreCase(currentValue)) {
+            flagsToStage.put(flagName, stagedValue);
+          }
+        }
+
+        if (!flagsToStage.isEmpty()) {
+          propsToStage.put(actualNamespace, flagsToStage);
+        }
+      }
+
+      return propsToStage;
+    }
+
     private void setProperty(String key, String value) {
         // Check if need to clear the property
         if (value == null) {
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index 94baf88..2285826 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -24,6 +24,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
+import android.companion.virtual.VirtualDeviceManager;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
@@ -134,7 +135,7 @@
         AppOpsManager.OpEventProxyInfo proxyInfo = null;
         if (proxyUid != Process.INVALID_UID) {
             proxyInfo = mAppOpsService.mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName,
-                    proxyAttributionTag);
+                            proxyAttributionTag, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
         }
 
         AppOpsManager.NoteOpEvent existingEvent = mAccessEvents.get(key);
@@ -855,7 +856,7 @@
             AppOpsManager.OpEventProxyInfo proxyInfo = null;
             if (proxyUid != Process.INVALID_UID) {
                 proxyInfo = mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName,
-                        proxyAttributionTag);
+                        proxyAttributionTag, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
             }
 
             if (recycled != null) {
@@ -881,10 +882,11 @@
 
         AppOpsManager.OpEventProxyInfo acquire(@IntRange(from = 0) int uid,
                 @Nullable String packageName,
-                @Nullable String attributionTag) {
+                @Nullable String attributionTag,
+                @Nullable String deviceId) {
             AppOpsManager.OpEventProxyInfo recycled = acquire();
             if (recycled != null) {
-                recycled.reinit(uid, packageName, attributionTag);
+                recycled.reinit(uid, packageName, attributionTag, deviceId);
                 return recycled;
             }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 559462a..b0e7575 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -218,6 +218,7 @@
     }
 
     @VisibleForTesting @Nullable protected AidlSession getSessionForUser(int userId) {
+        Slog.d(TAG, "getSessionForUser: mCurrentSession: " + mCurrentSession);
         if (mCurrentSession != null && mCurrentSession.getUserId() == userId) {
             return mCurrentSession;
         } else {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index b7e3f70..1c6dfe0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -242,6 +242,7 @@
     }
 
     @Nullable protected AidlSession getSessionForUser(int userId) {
+        Slog.d(TAG, "getSessionForUser: mCurrentSession: " + mCurrentSession);
         if (mCurrentSession != null && mCurrentSession.getUserId() == userId) {
             return mCurrentSession;
         } else {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 68e2bd6..7106e89 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2764,21 +2764,9 @@
                 display.setHasContentLocked(hasContent);
                 shouldScheduleTraversal = true;
             }
-            if (requestedModeId == 0 && requestedRefreshRate != 0) {
-                // Scan supported modes returned by display.getInfo() to find a mode with the same
-                // size as the default display mode but with the specified refresh rate instead.
-                Display.Mode mode = display.getDisplayInfoLocked().findDefaultModeByRefreshRate(
-                        requestedRefreshRate);
-                if (mode != null) {
-                    requestedModeId = mode.getModeId();
-                } else {
-                    Slog.e(TAG, "Couldn't find a mode for the requestedRefreshRate: "
-                            + requestedRefreshRate + " on Display: " + displayId);
-                }
-            }
 
-            mDisplayModeDirector.getAppRequestObserver().setAppRequest(
-                    displayId, requestedModeId, requestedMinRefreshRate, requestedMaxRefreshRate);
+            mDisplayModeDirector.getAppRequestObserver().setAppRequest(displayId, requestedModeId,
+                    requestedRefreshRate, requestedMinRefreshRate, requestedMaxRefreshRate);
 
             // TODO(b/202378408) set minimal post-processing only if it's supported once we have a
             // separate API for disabling on-device processing.
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index d3de71e..cd07f5a 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -159,6 +159,11 @@
             Flags::enablePeakRefreshRatePhysicalLimit
     );
 
+    private final FlagState mIgnoreAppPreferredRefreshRate = new FlagState(
+            Flags.FLAG_IGNORE_APP_PREFERRED_REFRESH_RATE_REQUEST,
+            Flags::ignoreAppPreferredRefreshRateRequest
+    );
+
     /**
      * @return {@code true} if 'port' is allowed in display layout configuration file.
      */
@@ -322,6 +327,13 @@
     }
 
     /**
+     * @return Whether to ignore preferredRefreshRate app request or not
+     */
+    public boolean ignoreAppPreferredRefreshRateRequest() {
+        return mIgnoreAppPreferredRefreshRate.isEnabled();
+    }
+
+    /**
      * dumps all flagstates
      * @param pw printWriter
      */
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 3d64fc2..a15a8e8 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -255,3 +255,14 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "ignore_app_preferred_refresh_rate_request"
+    namespace: "display_manager"
+    description: "Feature flag for DisplayManager to ignore preferred refresh rate app request. It will be handled by SF only."
+    bug: "330810426"
+    is_fixed_read_only: true
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 3084dae..91bd80e 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -222,7 +222,7 @@
                 displayManagerFlags.isRefreshRateVotingTelemetryEnabled());
         mSupportedModesByDisplay = new SparseArray<>();
         mDefaultModeByDisplay = new SparseArray<>();
-        mAppRequestObserver = new AppRequestObserver();
+        mAppRequestObserver = new AppRequestObserver(displayManagerFlags);
         mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig());
         mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
         mSettingsObserver = new SettingsObserver(context, handler, displayManagerFlags);
@@ -1205,17 +1205,32 @@
     public final class AppRequestObserver {
         private final SparseArray<Display.Mode> mAppRequestedModeByDisplay;
         private final SparseArray<RefreshRateRange> mAppPreferredRefreshRateRangeByDisplay;
+        private final boolean mIgnorePreferredRefreshRate;
 
-        AppRequestObserver() {
+        AppRequestObserver(DisplayManagerFlags flags) {
             mAppRequestedModeByDisplay = new SparseArray<>();
             mAppPreferredRefreshRateRangeByDisplay = new SparseArray<>();
+            mIgnorePreferredRefreshRate = flags.ignoreAppPreferredRefreshRateRequest();
         }
 
         /**
          * Sets refresh rates from app request
          */
-        public void setAppRequest(int displayId, int modeId,
+        public void setAppRequest(int displayId, int modeId, float requestedRefreshRate,
                 float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) {
+
+            if (modeId == 0 && requestedRefreshRate != 0 && !mIgnorePreferredRefreshRate) {
+                // Scan supported modes returned to find a mode with the same
+                // size as the default display mode but with the specified refresh rate instead.
+                Display.Mode mode = findDefaultModeByRefreshRate(displayId, requestedRefreshRate);
+                if (mode != null) {
+                    modeId = mode.getModeId();
+                } else {
+                    Slog.e(TAG, "Couldn't find a mode for the requestedRefreshRate: "
+                            + requestedRefreshRate + " on Display: " + displayId);
+                }
+            }
+
             synchronized (mLock) {
                 setAppRequestedModeLocked(displayId, modeId);
                 setAppPreferredRefreshRateRangeLocked(displayId, requestedMinRefreshRateRange,
@@ -1223,6 +1238,23 @@
             }
         }
 
+        @Nullable
+        private Display.Mode findDefaultModeByRefreshRate(int displayId, float refreshRate) {
+            Display.Mode[] modes;
+            Display.Mode defaultMode;
+            synchronized (mLock) {
+                modes = mSupportedModesByDisplay.get(displayId);
+                defaultMode = mDefaultModeByDisplay.get(displayId);
+            }
+            for (int i = 0; i < modes.length; i++) {
+                if (modes[i].matches(defaultMode.getPhysicalWidth(),
+                        defaultMode.getPhysicalHeight(), refreshRate)) {
+                    return modes[i];
+                }
+            }
+            return null;
+        }
+
         private void setAppRequestedModeLocked(int displayId, int modeId) {
             final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId);
             if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
diff --git a/services/core/java/com/android/server/dreams/Android.bp b/services/core/java/com/android/server/dreams/Android.bp
new file mode 100644
index 0000000..4078a42
--- /dev/null
+++ b/services/core/java/com/android/server/dreams/Android.bp
@@ -0,0 +1,11 @@
+aconfig_declarations {
+    name: "dreams_flags",
+    package: "com.android.server.dreams",
+    container: "system",
+    srcs: ["*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "dreams_flags_lib",
+    aconfig_declarations: "dreams_flags",
+}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index fc63494..2def5ae 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -45,6 +45,7 @@
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.net.Uri;
 import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -118,6 +119,7 @@
     private final DreamController mController;
     private final PowerManager mPowerManager;
     private final PowerManagerInternal mPowerManagerInternal;
+    private final BatteryManagerInternal mBatteryManagerInternal;
     private final PowerManager.WakeLock mDozeWakeLock;
     private final ActivityTaskManagerInternal mAtmInternal;
     private final PackageManagerInternal mPmInternal;
@@ -186,7 +188,11 @@
     private final BroadcastReceiver mChargingReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            mIsCharging = (BatteryManager.ACTION_CHARGING.equals(intent.getAction()));
+            if (Flags.useBatteryChangedBroadcast()) {
+                mIsCharging = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+            } else {
+                mIsCharging = (BatteryManager.ACTION_CHARGING.equals(intent.getAction()));
+            }
         }
     };
 
@@ -251,6 +257,12 @@
                 com.android.internal.R.bool.config_keepDreamingWhenUnplugging);
         mDreamsDisabledByAmbientModeSuppressionConfig = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig);
+
+        if (Flags.useBatteryChangedBroadcast()) {
+            mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
+        } else {
+            mBatteryManagerInternal = null;
+        }
     }
 
     @Override
@@ -279,9 +291,15 @@
 
             mContext.registerReceiver(
                     mDockStateReceiver, new IntentFilter(Intent.ACTION_DOCK_EVENT));
+
             IntentFilter chargingIntentFilter = new IntentFilter();
-            chargingIntentFilter.addAction(BatteryManager.ACTION_CHARGING);
-            chargingIntentFilter.addAction(BatteryManager.ACTION_DISCHARGING);
+            if (Flags.useBatteryChangedBroadcast()) {
+                chargingIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
+                chargingIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+            } else {
+                chargingIntentFilter.addAction(BatteryManager.ACTION_CHARGING);
+                chargingIntentFilter.addAction(BatteryManager.ACTION_DISCHARGING);
+            }
             mContext.registerReceiver(mChargingReceiver, chargingIntentFilter);
 
             mSettingsObserver = new SettingsObserver(mHandler);
diff --git a/services/core/java/com/android/server/dreams/flags.aconfig b/services/core/java/com/android/server/dreams/flags.aconfig
new file mode 100644
index 0000000..5d35ebd
--- /dev/null
+++ b/services/core/java/com/android/server/dreams/flags.aconfig
@@ -0,0 +1,12 @@
+package: "com.android.server.dreams"
+container: "system"
+
+flag {
+    name: "use_battery_changed_broadcast"
+    namespace: "communal"
+    description: "Use ACTION_BATTERY_CHANGED broadcast to track charging state"
+    bug: "329125239"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 3bd0a9f..326ef7e0 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -98,7 +98,10 @@
         final int size = imList.size();
         mIms = new InputMethodInfo[size];
         mSubtypeIds = new int[size];
-        int checkedItem = 0;
+        // No items are checked by default. When we have a list of explicitly enabled subtypes,
+        // the implicit subtype is no longer listed, but if it is still the selected one,
+        // no items will be shown as checked.
+        int checkedItem = -1;
         for (int i = 0; i < size; ++i) {
             final ImeSubtypeListItem item = imList.get(i);
             mIms[i] = item.mImi;
@@ -113,6 +116,12 @@
             }
         }
 
+        if (checkedItem == -1) {
+            Slog.w(TAG, "Switching menu shown with no item selected"
+                    + ", IME id: " + preferredInputMethodId
+                    + ", subtype index: " + preferredInputMethodSubtypeId);
+        }
+
         if (mDialogWindowContext == null) {
             mDialogWindowContext = new InputMethodDialogWindowContext();
         }
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index 96f32f3..bf49671 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -16,7 +16,7 @@
 
 package com.android.server.notification;
 
-import static android.app.Flags.updateRankingTime;
+import static android.app.Flags.sortSectionByTime;
 import static android.app.Notification.FLAG_INSISTENT;
 import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
@@ -497,7 +497,7 @@
                     Slog.v(TAG, "INTERRUPTIVENESS: "
                             + record.getKey() + " is interruptive: alerted");
                 }
-                if (updateRankingTime()) {
+                if (sortSectionByTime()) {
                     if (buzz || beep) {
                         record.resetRankingTime();
                     }
@@ -1528,7 +1528,7 @@
 
             // recent conversation
             if (record.isConversation()
-                    && record.getNotification().when > mLastAvalancheTriggerTimestamp) {
+                    && record.getNotification().getWhen() > mLastAvalancheTriggerTimestamp) {
                 return true;
             }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 9d4ab11..ca6ae63 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -25,6 +25,7 @@
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR;
 import static android.app.Flags.lifetimeExtensionRefactor;
+import static android.app.Flags.sortSectionByTime;
 import static android.app.Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
 import static android.app.Notification.EXTRA_BUILDER_APPLICATION_INFO;
 import static android.app.Notification.EXTRA_LARGE_ICON_BIG;
@@ -8593,7 +8594,7 @@
                         r.isUpdate = true;
                         final boolean isInterruptive = isVisuallyInterruptive(old, r);
                         r.setTextChanged(isInterruptive);
-                        if (android.app.Flags.updateRankingTime()) {
+                        if (sortSectionByTime()) {
                             if (isInterruptive) {
                                 r.resetRankingTime();
                             }
@@ -8738,7 +8739,7 @@
             return false;
         }
 
-        if (android.app.Flags.updateRankingTime()) {
+        if (sortSectionByTime()) {
             // Ignore visual interruptions from FGS/UIJs because users
             // consider them one 'session'. Count them for everything else.
             if (r.getSbn().getNotification().isFgsOrUij()) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 38c95f7..0c6a6c8 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -18,7 +18,7 @@
 import static android.app.Flags.restrictAudioAttributesAlarm;
 import static android.app.Flags.restrictAudioAttributesCall;
 import static android.app.Flags.restrictAudioAttributesMedia;
-import static android.app.Flags.updateRankingTime;
+import static android.app.Flags.sortSectionByTime;
 import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -580,7 +580,7 @@
         pw.println(prefix + "deleteIntent=" + notification.deleteIntent);
         pw.println(prefix + "number=" + notification.number);
         pw.println(prefix + "groupAlertBehavior=" + notification.getGroupAlertBehavior());
-        pw.println(prefix + "when=" + notification.when);
+        pw.println(prefix + "when=" + notification.when + "/" + notification.getWhen());
 
         pw.print(prefix + "tickerText=");
         if (!TextUtils.isEmpty(notification.tickerText)) {
@@ -1092,9 +1092,9 @@
     private long calculateRankingTimeMs(long previousRankingTimeMs) {
         Notification n = getNotification();
         // Take developer provided 'when', unless it's in the future.
-        if (updateRankingTime()) {
-            if (n.hasAppProvidedWhen() && n.when <= getSbn().getPostTime()){
-                return n.when;
+        if (sortSectionByTime()) {
+            if (n.hasAppProvidedWhen() && n.getWhen() <= getSbn().getPostTime()){
+                return n.getWhen();
             }
         } else {
             if (n.when != 0 && n.when <= getSbn().getPostTime()) {
@@ -1211,7 +1211,7 @@
     }
 
     public void resetRankingTime() {
-        if (updateRankingTime()) {
+        if (sortSectionByTime()) {
             mRankingTimeMs = calculateRankingTimeMs(getSbn().getPostTime());
         }
     }
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 9a6ea2c2..65ef53f 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -542,7 +542,7 @@
             this.is_locked = p.r.isLocked();
 
             this.age_in_minutes = NotificationRecordLogger.getAgeInMinutes(
-                    p.r.getSbn().getPostTime(), p.r.getSbn().getNotification().when);
+                    p.r.getSbn().getPostTime(), p.r.getSbn().getNotification().getWhen());
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 1f2ad07e..309e945 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -26,6 +26,7 @@
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 
+import static android.os.UserHandle.USER_SYSTEM;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
@@ -139,6 +140,8 @@
     private static final String ATT_VERSION = "version";
     private static final String ATT_NAME = "name";
     private static final String ATT_UID = "uid";
+
+    private static final String ATT_USERID = "userid";
     private static final String ATT_ID = "id";
     private static final String ATT_ALLOW_BUBBLE = "allow_bubble";
     private static final String ATT_PRIORITY = "priority";
@@ -268,7 +271,7 @@
                 }
                 if (type == XmlPullParser.START_TAG) {
                     if (TAG_STATUS_ICONS.equals(tag)) {
-                        if (forRestore && userId != UserHandle.USER_SYSTEM) {
+                        if (forRestore && userId != USER_SYSTEM) {
                             continue;
                         }
                         mHideSilentStatusBarIcons = parser.getAttributeBoolean(null,
@@ -311,8 +314,16 @@
                     : parser.getAttributeInt(null, ATT_ALLOW_BUBBLE, DEFAULT_BUBBLE_PREFERENCE);
             int appImportance = parser.getAttributeInt(null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
 
+            // when data is loaded from disk it's loaded as USER_ALL, but restored data that
+            // is pending app install needs the user id that the data was restored to
+            int fixedUserId = userId;
+            if (Flags.persistIncompleteRestoreData()) {
+                if (!forRestore && uid == UNKNOWN_UID) {
+                    fixedUserId = parser.getAttributeInt(null, ATT_USERID, USER_SYSTEM);
+                }
+            }
             PackagePreferences r = getOrCreatePackagePreferencesLocked(
-                    name, userId, uid,
+                    name, fixedUserId, uid,
                     appImportance,
                     parser.getAttributeInt(null, ATT_PRIORITY, DEFAULT_PRIORITY),
                     parser.getAttributeInt(null, ATT_VISIBILITY, DEFAULT_VISIBILITY),
@@ -504,6 +515,9 @@
             }
 
             if (r.uid == UNKNOWN_UID) {
+                if (Flags.persistIncompleteRestoreData()) {
+                    r.userId = userId;
+                }
                 mRestoredWithoutUids.put(unrestoredPackageKey(pkg, userId), r);
             } else {
                 mPackagePreferences.put(key, r);
@@ -674,6 +688,7 @@
 
         if (Flags.persistIncompleteRestoreData() && r.uid == UNKNOWN_UID) {
             out.attributeLong(null, ATT_CREATION_TIME, r.creationTime);
+            out.attributeInt(null, ATT_USERID, r.userId);
         }
 
         if (!forBackup) {
@@ -2947,6 +2962,8 @@
         boolean migrateToPm = false;
         long creationTime;
 
+        @UserIdInt int userId;
+
         Delegate delegate = null;
         ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
         Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 7756801..03dd935 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -147,11 +147,9 @@
                 if (sortSectionByTime()) {
                     final String groupKey = record.getGroupKey();
                     NotificationRecord existingProxy = mProxyByGroupTmp.get(groupKey);
-                    // summaries are mostly hidden in systemui - if there is a child notification
-                    // with better information, use its rank
-                    if (existingProxy == null
-                            || (existingProxy.getNotification().isGroupSummary()
-                            && !existingProxy.getNotification().hasAppProvidedWhen())) {
+                    // summaries are mostly hidden in systemui - if there is a child notification,
+                    // use its rank
+                    if (existingProxy == null || existingProxy.getNotification().isGroupSummary()) {
                         mProxyByGroupTmp.put(groupKey, record);
                     }
                 } else {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index b2c6c49..47ee1d0 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -513,7 +513,11 @@
                 // Legacy behavior to report appId as UID here.
                 // The final broadcasts will contain a per-user UID.
                 outInfo.mUid = ps.getAppId();
-                outInfo.mIsAppIdRemoved = true;
+                // Only send Intent.ACTION_UID_REMOVED when flag & DELETE_KEEP_DATA is 0
+                // i.e. the mDataRemoved is true
+                if (outInfo.mDataRemoved) {
+                    outInfo.mIsAppIdRemoved = true;
+                }
                 mPm.scheduleWritePackageRestrictions(user);
                 return;
             }
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 2e67b2f..9ab6016 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -170,6 +170,7 @@
             }
         }
 
+        boolean isPendingRestoreBefore = false;
         if (pkgSetting != null && oldSharedUserSetting != sharedUserSetting) {
             PackageManagerService.reportSettingsProblem(Log.WARN,
                     "Package " + parsedPackage.getPackageName() + " shared user changed from "
@@ -178,6 +179,9 @@
                             + " to "
                             + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
                             + "; replacing with new");
+            // Preserve the value of isPendingRestore. We need to set it to the new PackageSetting
+            // if the value is true to restore the app
+            isPendingRestoreBefore = pkgSetting.isPendingRestore();
             pkgSetting = null;
         }
 
@@ -224,6 +228,11 @@
                     parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
                     newDomainSetId,
                     parsedPackage.getTargetSdkVersion(), parsedPackage.getRestrictUpdateHash());
+
+            // If isPendingRestore is true before, set the value true to the PackageSetting
+            if (isPendingRestoreBefore) {
+                pkgSetting.setPendingRestore(true);
+            }
         } else {
             // make a deep copy to avoid modifying any existing system state.
             pkgSetting = new PackageSetting(pkgSetting);
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index 5e24673..00582bf 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -46,7 +46,7 @@
 /**
  * Launcher information used by {@link ShortcutService}.
  *
- * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
+ * All methods should be guarded by {@code ShortcutPackageItem#mPackageItemLock}.
  */
 class ShortcutLauncher extends ShortcutPackageItem {
     private static final String TAG = ShortcutService.TAG;
@@ -66,7 +66,7 @@
     /**
      * Package name -> IDs.
      */
-    @GuardedBy("mLock")
+    @GuardedBy("mPackageItemLock")
     private final ArrayMap<UserPackage, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
 
     private ShortcutLauncher(@NonNull ShortcutUser shortcutUser,
@@ -99,7 +99,7 @@
      */
     private void onRestoreBlocked() {
         final ArrayList<UserPackage> pinnedPackages;
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             pinnedPackages = new ArrayList<>(mPinnedShortcuts.keySet());
             mPinnedShortcuts.clear();
         }
@@ -138,7 +138,7 @@
 
         final int idSize = ids.size();
         if (idSize == 0) {
-            synchronized (mLock) {
+            synchronized (mPackageItemLock) {
                 mPinnedShortcuts.remove(up);
             }
         } else {
@@ -165,7 +165,7 @@
                     floatingSet.add(id);
                 }
             }
-            synchronized (mLock) {
+            synchronized (mPackageItemLock) {
                 final ArraySet<String> prevSet = mPinnedShortcuts.get(up);
                 if (prevSet != null) {
                     for (String id : floatingSet) {
@@ -187,7 +187,7 @@
     @Nullable
     public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName,
             @UserIdInt int packageUserId) {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             final ArraySet<String> pinnedShortcuts = mPinnedShortcuts.get(
                     UserPackage.of(packageUserId, packageName));
             return pinnedShortcuts == null ? null : new ArraySet<>(pinnedShortcuts);
@@ -198,7 +198,7 @@
      * Return true if the given shortcut is pinned by this launcher.<code></code>
      */
     public boolean hasPinned(ShortcutInfo shortcut) {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             final ArraySet<String> pinned = mPinnedShortcuts.get(
                     UserPackage.of(shortcut.getUserId(), shortcut.getPackage()));
             return (pinned != null) && pinned.contains(shortcut.getId());
@@ -211,7 +211,7 @@
     public void addPinnedShortcut(@NonNull String packageName, @UserIdInt int packageUserId,
             String id, boolean forPinRequest) {
         final ArrayList<String> pinnedList;
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             final ArraySet<String> pinnedSet = mPinnedShortcuts.get(
                     UserPackage.of(packageUserId, packageName));
             if (pinnedSet != null) {
@@ -227,7 +227,7 @@
     }
 
     boolean cleanUpPackage(String packageName, @UserIdInt int packageUserId) {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             return mPinnedShortcuts.remove(UserPackage.of(packageUserId, packageName)) != null;
         }
     }
@@ -253,7 +253,7 @@
             return;
         }
         final ArrayMap<UserPackage, ArraySet<String>> pinnedShortcuts;
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             pinnedShortcuts = new ArrayMap<>(mPinnedShortcuts);
         }
         final int size = pinnedShortcuts.size();
@@ -366,7 +366,7 @@
                                 : ShortcutService.parseIntAttribute(parser,
                                 ATTR_PACKAGE_USER_ID, ownerUserId);
                         ids = new ArraySet<>();
-                        synchronized (ret.mLock) {
+                        synchronized (ret.mPackageItemLock) {
                             ret.mPinnedShortcuts.put(
                                     UserPackage.of(packageUserId, packageName), ids);
                         }
@@ -407,7 +407,7 @@
         pw.println();
 
         final ArrayMap<UserPackage, ArraySet<String>> pinnedShortcuts;
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             pinnedShortcuts = new ArrayMap<>(mPinnedShortcuts);
         }
         final int size = pinnedShortcuts.size();
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 449e9ab..c929c1f 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -163,20 +163,20 @@
     /**
      * An in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs.
      */
-    @GuardedBy("mLock")
+    @GuardedBy("mPackageItemLock")
     private final ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
 
     /**
      * A temporary copy of shortcuts that are to be cleared once persisted into AppSearch, keyed on
      * IDs.
      */
-    @GuardedBy("mLock")
+    @GuardedBy("mPackageItemLock")
     private final ArrayMap<String, ShortcutInfo> mTransientShortcuts = new ArrayMap<>(0);
 
     /**
      * All the share targets from the package
      */
-    @GuardedBy("mLock")
+    @GuardedBy("mPackageItemLock")
     private final ArrayList<ShareTargetInfo> mShareTargets = new ArrayList<>(0);
 
     /**
@@ -193,10 +193,10 @@
 
     private long mLastKnownForegroundElapsedTime;
 
-    @GuardedBy("mLock")
+    @GuardedBy("mPackageItemLock")
     private long mLastReportedTime;
 
-    @GuardedBy("mLock")
+    @GuardedBy("mPackageItemLock")
     private boolean mIsAppSearchSchemaUpToDate;
 
     private ShortcutPackage(ShortcutUser shortcutUser,
@@ -233,7 +233,7 @@
     }
 
     public int getShortcutCount() {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             return mShortcuts.size();
         }
     }
@@ -276,7 +276,7 @@
     @Nullable
     public ShortcutInfo findShortcutById(@Nullable final String id) {
         if (id == null) return null;
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             return mShortcuts.get(id);
         }
     }
@@ -354,7 +354,7 @@
      */
     private ShortcutInfo forceDeleteShortcutInner(@NonNull String id) {
         final ShortcutInfo shortcut;
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             shortcut = mShortcuts.remove(id);
             if (shortcut != null) {
                 removeIcon(shortcut);
@@ -409,7 +409,7 @@
 
         if (newShortcut.isExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER)) {
             if (isAppSearchEnabled()) {
-                synchronized (mLock) {
+                synchronized (mPackageItemLock) {
                     mTransientShortcuts.put(newShortcut.getId(), newShortcut);
                 }
             }
@@ -482,7 +482,7 @@
 
         if (newShortcut.isExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER)) {
             if (isAppSearchEnabled()) {
-                synchronized (mLock) {
+                synchronized (mPackageItemLock) {
                     mTransientShortcuts.put(newShortcut.getId(), newShortcut);
                 }
             }
@@ -506,7 +506,7 @@
         final ShortcutService service = mShortcutUser.mService;
         // Ensure the total number of shortcuts doesn't exceed the hard limit per app.
         final int maxShortcutPerApp = service.getMaxAppShortcuts();
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             final List<ShortcutInfo> appShortcuts = mShortcuts.values().stream().filter(si ->
                     !si.isPinned()).collect(Collectors.toList());
             if (appShortcuts.size() >= maxShortcutPerApp) {
@@ -555,7 +555,7 @@
     public List<ShortcutInfo> deleteAllDynamicShortcuts() {
         final long now = mShortcutUser.mService.injectCurrentTimeMillis();
         boolean changed = false;
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             for (int i = mShortcuts.size() - 1; i >= 0; i--) {
                 ShortcutInfo si = mShortcuts.valueAt(i);
                 if (si.isDynamic() && si.isVisibleToPublisher()) {
@@ -914,7 +914,7 @@
 
     List<ShortcutManager.ShareShortcutInfo> getMatchingShareTargets(
             @NonNull final IntentFilter filter, @Nullable final String pkgName) {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             final List<ShareTargetInfo> matchedTargets = new ArrayList<>();
             for (int i = 0; i < mShareTargets.size(); i++) {
                 final ShareTargetInfo target = mShareTargets.get(i);
@@ -967,7 +967,7 @@
     }
 
     public boolean hasShareTargets() {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             return !mShareTargets.isEmpty();
         }
     }
@@ -978,7 +978,7 @@
      * the app's Xml resource.
      */
     int getSharingShortcutCount() {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             if (mShareTargets.isEmpty()) {
                 return 0;
             }
@@ -1017,7 +1017,7 @@
     /**
      * Return the filenames (excluding path names) of icon bitmap files from this package.
      */
-    @GuardedBy("mLock")
+    @GuardedBy("mPackageItemLock")
     private ArraySet<String> getUsedBitmapFilesLocked() {
         final ArraySet<String> usedFiles = new ArraySet<>(1);
         forEachShortcut(si -> {
@@ -1029,7 +1029,7 @@
     }
 
     public void cleanupDanglingBitmapFiles(@NonNull File path) {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             mShortcutBitmapSaver.waitForAllSavesLocked();
             final ArraySet<String> usedFiles = getUsedBitmapFilesLocked();
 
@@ -1136,7 +1136,7 @@
         // Now prepare to publish manifest shortcuts.
         List<ShortcutInfo> newManifestShortcutList = null;
         int shareTargetSize = 0;
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             try {
                 shareTargetSize = mShareTargets.size();
                 newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService,
@@ -1680,7 +1680,7 @@
 
     void reportShortcutUsed(@NonNull final UsageStatsManagerInternal usageStatsManagerInternal,
             @NonNull final String shortcutId) {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             final long currentTS = SystemClock.elapsedRealtime();
             final ShortcutService s = mShortcutUser.mService;
             if (currentTS - mLastReportedTime > s.mSaveDelayMillis) {
@@ -1757,7 +1757,7 @@
         pw.println(")");
 
         pw.println();
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             mShortcutBitmapSaver.dumpLocked(pw, "  ");
         }
     }
@@ -1827,7 +1827,7 @@
     @Override
     public void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup)
             throws IOException, XmlPullParserException {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             final int size = mShortcuts.size();
             final int shareTargetSize = mShareTargets.size();
 
@@ -2037,7 +2037,7 @@
 
         final ShortcutPackage ret = new ShortcutPackage(shortcutUser,
                 shortcutUser.getUserId(), packageName);
-        synchronized (ret.mLock) {
+        synchronized (ret.mPackageItemLock) {
             ret.mIsAppSearchSchemaUpToDate = ShortcutService.parseIntAttribute(
                     parser, ATTR_SCHEMA_VERSON, 0) == AppSearchShortcutInfo.SCHEMA_VERSION;
 
@@ -2283,7 +2283,7 @@
 
     @VisibleForTesting
     List<ShareTargetInfo> getAllShareTargetsForTest() {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             return new ArrayList<>(mShareTargets);
         }
     }
@@ -2404,7 +2404,7 @@
             @NonNull final Consumer<ShortcutInfo> transform) {
         Objects.requireNonNull(id);
         Objects.requireNonNull(transform);
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             if (shortcut != null) {
                 transform.accept(shortcut);
             }
@@ -2424,7 +2424,7 @@
 
     private void saveShortcut(@NonNull final Collection<ShortcutInfo> shortcuts) {
         Objects.requireNonNull(shortcuts);
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             for (ShortcutInfo si : shortcuts) {
                 mShortcuts.put(si.getId(), si);
             }
@@ -2433,7 +2433,7 @@
 
     @Nullable
     List<ShortcutInfo> findAll(@NonNull final Collection<String> ids) {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             return ids.stream().map(mShortcuts::get)
                     .filter(Objects::nonNull).collect(Collectors.toList());
         }
@@ -2455,7 +2455,7 @@
 
     private void forEachShortcutStopWhen(
             @NonNull final Function<ShortcutInfo, Boolean> cb) {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             for (int i = mShortcuts.size() - 1; i >= 0; i--) {
                 final ShortcutInfo si = mShortcuts.valueAt(i);
                 if (cb.apply(si)) {
@@ -2600,7 +2600,7 @@
                         })));
     }
 
-    @GuardedBy("mLock")
+    @GuardedBy("mPackageItemLock")
     @Override
     void scheduleSaveToAppSearchLocked() {
         final Map<String, ShortcutInfo> copy = new ArrayMap<>(mShortcuts);
@@ -2684,7 +2684,7 @@
                     .penaltyLog() // TODO: change this to penaltyDeath to fix the call-site
                     .build());
             future = mShortcutUser.getAppSearch(searchContext);
-            synchronized (mLock) {
+            synchronized (mPackageItemLock) {
                 if (!mIsAppSearchSchemaUpToDate) {
                     future = future.thenCompose(this::setupSchema);
                 }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 12115af..dfd2e08 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -39,7 +39,7 @@
 import java.util.Objects;
 
 /**
- * All methods should be either guarded by {@code #mShortcutUser.mService.mLock} or {@code #mLock}.
+ * All methods should be either guarded by {@code #mPackageItemLock}.
  */
 abstract class ShortcutPackageItem {
     private static final String TAG = ShortcutService.TAG;
@@ -52,10 +52,10 @@
 
     protected ShortcutUser mShortcutUser;
 
-    @GuardedBy("mLock")
+    @GuardedBy("mPackageItemLock")
     protected final ShortcutBitmapSaver mShortcutBitmapSaver;
 
-    protected final Object mLock = new Object();
+    protected final Object mPackageItemLock = new Object();
 
     protected ShortcutPackageItem(@NonNull ShortcutUser shortcutUser,
             int packageUserId, @NonNull String packageName,
@@ -157,7 +157,7 @@
     public abstract void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup)
             throws IOException, XmlPullParserException;
 
-    @GuardedBy("mLock")
+    @GuardedBy("mPackageItemLock")
     public void saveToFileLocked(File path, boolean forBackup) {
         try (ResilientAtomicFile file = getResilientFile(path)) {
             FileOutputStream os = null;
@@ -187,7 +187,7 @@
         }
     }
 
-    @GuardedBy("mLock")
+    @GuardedBy("mPackageItemLock")
     void scheduleSaveToAppSearchLocked() {
 
     }
@@ -219,7 +219,7 @@
         if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) {
             Slog.d(TAG, "Saving package item " + getPackageName() + " to " + path);
         }
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             path.getParentFile().mkdirs();
             // TODO: Since we are persisting shortcuts into AppSearch, we should read from/write to
             //  AppSearch as opposed to maintaining a separate XML file.
@@ -229,14 +229,14 @@
     }
 
     public boolean waitForBitmapSaves() {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             return mShortcutBitmapSaver.waitForAllSavesLocked();
         }
     }
 
     public void saveBitmap(ShortcutInfo shortcut,
             int maxDimension, Bitmap.CompressFormat format, int quality) {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             mShortcutBitmapSaver.saveBitmapLocked(shortcut, maxDimension, format, quality);
         }
     }
@@ -246,19 +246,19 @@
      */
     @Nullable
     public String getBitmapPathMayWait(ShortcutInfo shortcut) {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             return mShortcutBitmapSaver.getBitmapPathMayWaitLocked(shortcut);
         }
     }
 
     public void removeIcon(ShortcutInfo shortcut) {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             mShortcutBitmapSaver.removeIcon(shortcut);
         }
     }
 
     void removeShortcutPackageItem() {
-        synchronized (mLock) {
+        synchronized (mPackageItemLock) {
             getResilientFile(getShortcutPackageItemFile()).delete();
         }
     }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index fe9c3f2..3f5ec06 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -295,7 +295,7 @@
 
     final Context mContext;
 
-    private final Object mLock = new Object();
+    private final Object mServiceLock = new Object();
     private final Object mNonPersistentUsersLock = new Object();
     private final Object mWtfLock = new Object();
 
@@ -333,7 +333,7 @@
     /**
      * User ID -> UserShortcuts
      */
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     private final SparseArray<ShortcutUser> mUsers = new SparseArray<>();
 
     /**
@@ -388,13 +388,13 @@
     private final ShortcutRequestPinProcessor mShortcutRequestPinProcessor;
     private final ShortcutDumpFiles mShortcutDumpFiles;
 
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     final SparseIntArray mUidState = new SparseIntArray();
 
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     final SparseLongArray mUidLastForegroundElapsedTime = new SparseLongArray();
 
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     private List<Integer> mDirtyUserIds = new ArrayList<>();
 
     private final AtomicBoolean mBootCompleted = new AtomicBoolean();
@@ -473,7 +473,7 @@
     @GuardedBy("mWtfLock")
     private Exception mLastWtfStacktrace;
 
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     private final MetricsLogger mMetricsLogger = new MetricsLogger();
 
     private final boolean mIsAppSearchEnabled;
@@ -518,7 +518,7 @@
         mUriPermissionOwner = mUriGrantsManagerInternal.newUriPermissionOwner(TAG);
         mRoleManager = Objects.requireNonNull(mContext.getSystemService(RoleManager.class));
 
-        mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mLock);
+        mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mServiceLock);
         mShortcutDumpFiles = new ShortcutDumpFiles(this);
         mIsAppSearchEnabled = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.SHORTCUT_APPSEARCH_INTEGRATION, false)
@@ -595,7 +595,7 @@
         // Default launcher is removed or changed, revoke all URI permissions.
         mUriGrantsManagerInternal.revokeUriPermissionFromOwner(mUriPermissionOwner, null, ~0, 0);
 
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             // Clear the launcher cache for this user. It will be set again next time the default
             // launcher is read from RoleManager.
             if (isUserLoadedLocked(userId)) {
@@ -622,7 +622,7 @@
             Slog.d(TAG, "onUidStateChanged: uid=" + uid + " state=" + procState);
         }
         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleOnUidStateChanged");
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             mUidState.put(uid, procState);
 
             // We need to keep track of last time an app comes to foreground.
@@ -639,7 +639,7 @@
         return processState <= PROCESS_STATE_FOREGROUND_THRESHOLD;
     }
 
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     boolean isUidForegroundLocked(int uid) {
         if (uid == Process.SYSTEM_UID) {
             // IUidObserver doesn't report the state of SYSTEM, but it always has bound services,
@@ -655,7 +655,7 @@
         return isProcessStateForeground(mActivityManagerInternal.getUidProcessState(uid));
     }
 
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     long getUidLastForegroundElapsedTimeLocked(int uid) {
         return mUidLastForegroundElapsedTime.get(uid);
     }
@@ -729,7 +729,7 @@
         final long start = getStatStartTime();
         injectRunOnNewThread(() -> {
             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleUnlockUser");
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 logDurationStat(Stats.ASYNC_PRELOAD_USER_DELAY, start);
                 getUserShortcutsLocked(userId);
             }
@@ -743,7 +743,7 @@
             Slog.d(TAG, "handleStopUser: user=" + userId);
         }
         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleStopUser");
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             unloadUserLocked(userId);
 
             synchronized (mUnlockedUsers) {
@@ -753,7 +753,7 @@
         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
     }
 
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     private void unloadUserLocked(int userId) {
         if (DEBUG || DEBUG_REBOOT) {
             Slog.d(TAG, "unloadUserLocked: user=" + userId);
@@ -784,7 +784,7 @@
      * Init the instance. (load the state file, etc)
      */
     private void initialize() {
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             loadConfigurationLocked();
             loadBaseStateLocked();
         }
@@ -1003,7 +1003,7 @@
 
             FileOutputStream outs = null;
             try {
-                synchronized (mLock) {
+                synchronized (mServiceLock) {
                     outs = file.startWrite();
                 }
 
@@ -1029,7 +1029,7 @@
         }
     }
 
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     private void loadBaseStateLocked() {
         mRawLastResetTime.set(0);
 
@@ -1104,7 +1104,7 @@
                     Slog.d(TAG, "Saving to " + file);
                 }
 
-                synchronized (mLock) {
+                synchronized (mServiceLock) {
                     os = file.startWrite();
                     saveUserInternalLocked(userId, os, /* forBackup= */ false);
                 }
@@ -1122,7 +1122,7 @@
         getUserShortcutsLocked(userId).logSharingShortcutStats(mMetricsLogger);
     }
 
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os,
             boolean forBackup) throws IOException, XmlPullParserException {
 
@@ -1224,7 +1224,7 @@
         if (DEBUG || DEBUG_REBOOT) {
             Slog.d(TAG, "Scheduling to save for " + userId);
         }
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             if (!mDirtyUserIds.contains(userId)) {
                 mDirtyUserIds.add(userId);
             }
@@ -1245,7 +1245,7 @@
         try {
             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutSaveDirtyInfo");
             List<Integer> dirtyUserIds = new ArrayList<>();
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 List<Integer> tmp = mDirtyUserIds;
                 mDirtyUserIds = dirtyUserIds;
                 dirtyUserIds = tmp;
@@ -1266,14 +1266,14 @@
     }
 
     /** Return the last reset time. */
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     long getLastResetTimeLocked() {
         updateTimesLocked();
         return mRawLastResetTime.get();
     }
 
     /** Return the next reset time. */
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     long getNextResetTimeLocked() {
         updateTimesLocked();
         return mRawLastResetTime.get() + mResetInterval;
@@ -1286,7 +1286,7 @@
     /**
      * Update the last reset time.
      */
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     private void updateTimesLocked() {
 
         final long now = injectCurrentTimeMillis();
@@ -1315,7 +1315,7 @@
         }
     }
 
-    // Requires mLock held, but "Locked" prefix would look weired so we just say "L".
+    // Requires mServiceLock held, but "Locked" prefix would look weird so we just say "L".
     protected boolean isUserUnlockedL(@UserIdInt int userId) {
         // First, check the local copy.
         synchronized (mUnlockedUsers) {
@@ -1331,14 +1331,14 @@
         return mUserManagerInternal.isUserUnlockingOrUnlocked(userId);
     }
 
-    // Requires mLock held, but "Locked" prefix would look weired so we jsut say "L".
+    // Requires mServiceLock held, but "Locked" prefix would look weird so we just say "L".
     void throwIfUserLockedL(@UserIdInt int userId) {
         if (!isUserUnlockedL(userId)) {
             throw new IllegalStateException("User " + userId + " is locked or not running");
         }
     }
 
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     @NonNull
     private boolean isUserLoadedLocked(@UserIdInt int userId) {
         return mUsers.get(userId) != null;
@@ -1347,7 +1347,7 @@
     private int mLastLockedUser = -1;
 
     /** Return the per-user state. */
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     @NonNull
     ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) {
         if (!isUserUnlockedL(userId)) {
@@ -1386,7 +1386,7 @@
         return ret;
     }
 
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     void forEachLoadedUserLocked(@NonNull Consumer<ShortcutUser> c) {
         for (int i = mUsers.size() - 1; i >= 0; i--) {
             c.accept(mUsers.valueAt(i));
@@ -1397,7 +1397,7 @@
      * Return the per-user per-package state.  If the caller is a publisher, use
      * {@link #getPackageShortcutsForPublisherLocked} instead.
      */
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     @NonNull
     ShortcutPackage getPackageShortcutsLocked(
             @NonNull String packageName, @UserIdInt int userId) {
@@ -1405,7 +1405,7 @@
     }
 
     /** Return the per-user per-package state.  Use this when the caller is a publisher. */
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     @NonNull
     ShortcutPackage getPackageShortcutsForPublisherLocked(
             @NonNull String packageName, @UserIdInt int userId) {
@@ -1414,7 +1414,7 @@
         return ret;
     }
 
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     @NonNull
     ShortcutLauncher getLauncherShortcutsLocked(
             @NonNull String packageName, @UserIdInt int ownerUserId,
@@ -1443,7 +1443,7 @@
      * {@link ShortcutBitmapSaver#waitForAllSavesLocked()} to make sure there's no pending bitmap
      * saves are going on.
      */
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     private void cleanupDanglingBitmapDirectoriesLocked(@UserIdInt int userId) {
         if (DEBUG) {
             Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId);
@@ -1780,7 +1780,7 @@
     void injectPostToHandlerDebounced(@NonNull final Object token, @NonNull final Runnable r) {
         Objects.requireNonNull(token);
         Objects.requireNonNull(r);
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             mHandler.removeCallbacksAndMessages(token);
             mHandler.postDelayed(r, token, CALLBACK_DELAY);
         }
@@ -2015,7 +2015,7 @@
         List<ShortcutInfo> removedShortcuts = null;
         final ShortcutPackage ps;
 
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
 
             ps = getPackageShortcutsForPublisherLocked(packageName, userId);
@@ -2084,7 +2084,7 @@
         final List<ShortcutInfo> changedShortcuts = new ArrayList<>(1);
         final ShortcutPackage ps;
 
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
 
             ps = getPackageShortcutsForPublisherLocked(packageName, userId);
@@ -2184,7 +2184,7 @@
         List<ShortcutInfo> changedShortcuts = null;
         final ShortcutPackage ps;
 
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
 
             ps = getPackageShortcutsForPublisherLocked(packageName, userId);
@@ -2241,7 +2241,7 @@
         List<ShortcutInfo> removedShortcuts = null;
         final ShortcutPackage ps;
 
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
 
             ps = getPackageShortcutsForPublisherLocked(packageName, userId);
@@ -2306,7 +2306,7 @@
         verifyCaller(packageName, userId);
         verifyShortcutInfoPackage(packageName, shortcut);
         final Intent intent;
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
             // Send request to the launcher, if supported.
             intent = mShortcutRequestPinProcessor.createShortcutResultIntent(shortcut, userId);
@@ -2337,7 +2337,7 @@
         }
 
         final boolean ret;
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
 
             Preconditions.checkState(isUidForegroundLocked(callingUid),
@@ -2378,7 +2378,7 @@
         List<ShortcutInfo> changedShortcuts = null;
         List<ShortcutInfo> removedShortcuts = null;
         final ShortcutPackage ps;
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
             ps = getPackageShortcutsForPublisherLocked(packageName, userId);
             ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds,
@@ -2419,7 +2419,7 @@
         Objects.requireNonNull(shortcutIds, "shortcutIds must be provided");
         List<ShortcutInfo> changedShortcuts = null;
         final ShortcutPackage ps;
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
             ps = getPackageShortcutsForPublisherLocked(packageName, userId);
             ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds,
@@ -2449,7 +2449,7 @@
         List<ShortcutInfo> changedShortcuts = null;
         List<ShortcutInfo> removedShortcuts = null;
         final ShortcutPackage ps;
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
             ps = getPackageShortcutsForPublisherLocked(packageName, userId);
             ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds,
@@ -2487,7 +2487,7 @@
         List<ShortcutInfo> changedShortcuts = new ArrayList<>();
         List<ShortcutInfo> removedShortcuts = null;
         final ShortcutPackage ps;
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
             ps = getPackageShortcutsForPublisherLocked(packageName, userId);
             // Dynamic shortcuts that are either cached or pinned will not get deleted.
@@ -2511,7 +2511,7 @@
         List<ShortcutInfo> changedShortcuts = null;
         List<ShortcutInfo> removedShortcuts = null;
         final ShortcutPackage ps;
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
             ps = getPackageShortcutsForPublisherLocked(packageName, userId);
             ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds,
@@ -2545,7 +2545,7 @@
     public ParceledListSlice<ShortcutInfo> getShortcuts(String packageName,
             @ShortcutManager.ShortcutMatchFlags int matchFlags, @UserIdInt int userId) {
         verifyCaller(packageName, userId);
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
             final boolean matchDynamic = (matchFlags & ShortcutManager.FLAG_MATCH_DYNAMIC) != 0;
             final boolean matchPinned = (matchFlags & ShortcutManager.FLAG_MATCH_PINNED) != 0;
@@ -2575,7 +2575,7 @@
                 "getShareTargets");
         final ComponentName chooser = injectChooserActivity();
         final String pkg = chooser != null ? chooser.getPackageName() : mContext.getPackageName();
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
             final List<ShortcutManager.ShareShortcutInfo> shortcutInfoList = new ArrayList<>();
             final ShortcutUser user = getUserShortcutsLocked(userId);
@@ -2592,7 +2592,7 @@
         enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS,
                 "hasShareTargets");
 
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
 
             return getPackageShortcutsLocked(packageToCheck, userId).hasShareTargets();
@@ -2606,7 +2606,7 @@
         enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS,
                 "isSharingShortcut");
 
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
             throwIfUserLockedL(callingUserId);
 
@@ -2623,7 +2623,7 @@
         return false;
     }
 
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
             @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> filter) {
 
@@ -2649,7 +2649,7 @@
         final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
                 injectBinderCallingPid(), injectBinderCallingUid());
 
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
 
             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
@@ -2661,7 +2661,7 @@
     public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
         verifyCaller(packageName, userId);
 
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
 
             return getNextResetTimeLocked();
@@ -2672,7 +2672,7 @@
     public int getIconMaxDimensions(String packageName, int userId) {
         verifyCaller(packageName, userId);
 
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             return mMaxIconDimension;
         }
     }
@@ -2686,7 +2686,7 @@
                     shortcutId, packageName, userId));
         }
         final ShortcutPackage ps;
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
             ps = getPackageShortcutsForPublisherLocked(packageName, userId);
             if (ps.findShortcutById(shortcutId) == null) {
@@ -2723,7 +2723,7 @@
     }
 
     void resetThrottlingInner(@UserIdInt int userId) {
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             if (!isUserUnlockedL(userId)) {
                 Log.w(TAG, "User " + userId + " is locked or not running");
                 return;
@@ -2747,7 +2747,7 @@
             Slog.d(TAG, "onApplicationActive: package=" + packageName + "  userid=" + userId);
         }
         enforceResetThrottlingPermission();
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             if (!isUserUnlockedL(userId)) {
                 // This is called by system UI, so no need to throw.  Just ignore.
                 return;
@@ -2804,7 +2804,7 @@
     // even when hasShortcutPermission() is overridden.
     @VisibleForTesting
     boolean hasShortcutHostPermissionInner(@NonNull String packageName, int userId) {
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             throwIfUserLockedL(userId);
 
             final String defaultLauncher = getDefaultLauncher(userId);
@@ -2830,7 +2830,7 @@
         final long token = injectClearCallingIdentity();
         boolean isSupported;
         try {
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 isSupported = !mUserManagerInternal.getUserProperties(userId)
                         .areItemsRestrictedOnHomeScreen();
             }
@@ -2846,7 +2846,7 @@
         final long start = getStatStartTime();
         final long token = injectClearCallingIdentity();
         try {
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 throwIfUserLockedL(userId);
 
                 final ShortcutUser user = getUserShortcutsLocked(userId);
@@ -2890,7 +2890,7 @@
 
     private void cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId,
             boolean appStillExists) {
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             forEachLoadedUserLocked(user ->
                     cleanUpPackageLocked(packageName, user.getUserId(), packageUserId,
                             appStillExists));
@@ -2904,7 +2904,7 @@
      *
      * This is called when an app is uninstalled, or an app gets "clear data"ed.
      */
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     @VisibleForTesting
     void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId,
             boolean appStillExists) {
@@ -2979,7 +2979,7 @@
                 shortcutIds = null; // LauncherAppsService already threw for it though.
             }
 
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 throwIfUserLockedL(userId);
                 throwIfUserLockedL(launcherUserId);
 
@@ -3005,7 +3005,7 @@
             return setReturnedByServer(ret);
         }
 
-        @GuardedBy("ShortcutService.this.mLock")
+        @GuardedBy("ShortcutService.this.mServiceLock")
         private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
                 @Nullable String packageName, @Nullable List<String> shortcutIds,
                 @Nullable List<LocusId> locusIds, long changedSince,
@@ -3095,7 +3095,7 @@
                 return;
             }
             final ShortcutPackage p;
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 p = getUserShortcutsLocked(userId).getPackageShortcutsIfExists(packageName);
             }
             if (p == null) {
@@ -3129,7 +3129,7 @@
             Preconditions.checkStringNotEmpty(packageName, "packageName");
             Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
 
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 throwIfUserLockedL(userId);
                 throwIfUserLockedL(launcherUserId);
 
@@ -3143,7 +3143,7 @@
             }
         }
 
-        @GuardedBy("ShortcutService.this.mLock")
+        @GuardedBy("ShortcutService.this.mServiceLock")
         private ShortcutInfo getShortcutInfoLocked(
                 int launcherUserId, @NonNull String callingPackage,
                 @NonNull String packageName, @NonNull String shortcutId, int userId,
@@ -3176,7 +3176,7 @@
             throwIfUserLockedL(launcherUserId);
 
             final ShortcutPackage p;
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 p = getUserShortcutsLocked(userId).getPackageShortcutsIfExists(packageName);
             }
             if (p == null) {
@@ -3198,7 +3198,7 @@
             List<ShortcutInfo> changedShortcuts = null;
             List<ShortcutInfo> removedShortcuts = null;
             final ShortcutPackage sp;
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 throwIfUserLockedL(userId);
                 throwIfUserLockedL(launcherUserId);
 
@@ -3284,7 +3284,7 @@
             List<ShortcutInfo> changedShortcuts = null;
             List<ShortcutInfo> removedShortcuts = null;
             final ShortcutPackage sp;
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 throwIfUserLockedL(userId);
                 throwIfUserLockedL(launcherUserId);
 
@@ -3346,7 +3346,7 @@
             Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
             Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
 
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 throwIfUserLockedL(userId);
                 throwIfUserLockedL(launcherUserId);
 
@@ -3380,7 +3380,7 @@
             Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
 
             // Check in memory shortcut first
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 throwIfUserLockedL(userId);
                 throwIfUserLockedL(launcherUserId);
 
@@ -3430,7 +3430,7 @@
             Objects.requireNonNull(packageName, "packageName");
             Objects.requireNonNull(shortcutId, "shortcutId");
 
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 throwIfUserLockedL(userId);
                 throwIfUserLockedL(launcherUserId);
 
@@ -3458,7 +3458,7 @@
             Objects.requireNonNull(packageName, "packageName");
             Objects.requireNonNull(shortcutId, "shortcutId");
 
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 throwIfUserLockedL(userId);
                 throwIfUserLockedL(launcherUserId);
 
@@ -3484,7 +3484,7 @@
             Objects.requireNonNull(packageName, "packageName");
             Objects.requireNonNull(shortcutId, "shortcutId");
 
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 throwIfUserLockedL(userId);
                 throwIfUserLockedL(launcherUserId);
 
@@ -3515,7 +3515,7 @@
 
             // Checks shortcuts in memory first
             final ShortcutPackage p;
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 throwIfUserLockedL(userId);
                 throwIfUserLockedL(launcherUserId);
 
@@ -3568,7 +3568,7 @@
             Objects.requireNonNull(packageName, "packageName");
             Objects.requireNonNull(shortcutId, "shortcutId");
 
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 throwIfUserLockedL(userId);
                 throwIfUserLockedL(launcherUserId);
 
@@ -3599,7 +3599,7 @@
             Objects.requireNonNull(shortcutId, "shortcutId");
 
             // Checks shortcuts in memory first
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 throwIfUserLockedL(userId);
                 throwIfUserLockedL(launcherUserId);
 
@@ -3702,7 +3702,7 @@
             if (!callingPackage.equals(defaultLauncher)) {
                 return false;
             }
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 if (!isUidForegroundLocked(callingUid)) {
                     return false;
                 }
@@ -3733,7 +3733,7 @@
         }
         scheduleSaveBaseState();
 
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             final long token = injectClearCallingIdentity();
             try {
                 forEachLoadedUserLocked(user -> user.detectLocaleChange());
@@ -3762,7 +3762,7 @@
             // but we still check it in unit tests.
             final long token = injectClearCallingIdentity();
             try {
-                synchronized (mLock) {
+                synchronized (mServiceLock) {
                     if (!isUserUnlockedL(userId)) {
                         if (DEBUG) {
                             Slog.d(TAG, "Ignoring package broadcast " + action
@@ -3821,7 +3821,7 @@
             // Since it cleans up the shortcut directory and rewrite the ShortcutPackageItems
             // in odrder during saveToXml(), it could lead to shortcuts missing when shutdown.
             // We need it so that it can finish up saving before shutdown.
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 if (mHandler.hasCallbacks(mSaveDirtyInfoRunner)) {
                     mHandler.removeCallbacks(mSaveDirtyInfoRunner);
                     forEachLoadedUserLocked(ShortcutUser::cancelAllInFlightTasks);
@@ -3852,7 +3852,7 @@
         try {
             final ArrayList<UserPackage> gonePackages = new ArrayList<>();
 
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 final ShortcutUser user = getUserShortcutsLocked(ownerUserId);
 
                 // Find packages that have been uninstalled.
@@ -3885,7 +3885,7 @@
         verifyStates();
     }
 
-    @GuardedBy("mLock")
+    @GuardedBy("mServiceLock")
     private void rescanUpdatedPackagesLocked(@UserIdInt int userId, long lastScanTime) {
         if (DEBUG_REBOOT) {
             Slog.d(TAG, "rescan updated package user=" + userId + " last scanned=" + lastScanTime);
@@ -3916,7 +3916,7 @@
         if (DEBUG || DEBUG_REBOOT) {
             Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
         }
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             final ShortcutUser user = getUserShortcutsLocked(userId);
             user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
             user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
@@ -3929,7 +3929,7 @@
             Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d",
                     packageName, userId));
         }
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             final ShortcutUser user = getUserShortcutsLocked(userId);
             user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
 
@@ -3972,7 +3972,7 @@
         }
 
         // Activities may be disabled or enabled.  Just rescan the package.
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             final ShortcutUser user = getUserShortcutsLocked(packageUserId);
 
             user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
@@ -4474,7 +4474,7 @@
         if (DEBUG) {
             Slog.d(TAG, "Backing up user " + userId);
         }
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             if (!isUserUnlockedL(userId)) {
                 wtf("Can't backup: user " + userId + " is locked or not running");
                 return null;
@@ -4524,7 +4524,7 @@
         if (DEBUG || DEBUG_REBOOT) {
             Slog.d(TAG, "Restoring user " + userId);
         }
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             if (!isUserUnlockedL(userId)) {
                 wtf("Can't restore: user " + userId + " is locked or not running");
                 return;
@@ -4762,7 +4762,7 @@
     }
 
     private void dumpInner(PrintWriter pw, DumpFilter filter) {
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             if (filter.shouldDumpDetails()) {
                 final long now = injectCurrentTimeMillis();
                 pw.print("Now: [");
@@ -4841,7 +4841,7 @@
     }
 
     private void dumpUid(PrintWriter pw) {
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             pw.println("** SHORTCUT MANAGER UID STATES (dumpsys shortcut -n -u)");
 
             for (int i = 0; i < mUidState.size(); i++) {
@@ -4876,7 +4876,7 @@
      * behavior but shortcut service doesn't for now.
      */
     private  void dumpCheckin(PrintWriter pw, boolean clear) {
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             try {
                 final JSONArray users = new JSONArray();
 
@@ -4898,7 +4898,7 @@
     }
 
     private void dumpDumpFiles(PrintWriter pw) {
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             pw.println("** SHORTCUT MANAGER FILES (dumpsys shortcut -n -f)");
             mShortcutDumpFiles.dumpAll(pw);
         }
@@ -5051,7 +5051,7 @@
         }
 
         private void handleResetThrottling() throws CommandException {
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 parseOptionsLocked(/* takeUser =*/ true);
 
                 Slog.i(TAG, "cmd: handleResetThrottling: user=" + mUserId);
@@ -5071,7 +5071,7 @@
 
             Slog.i(TAG, "cmd: handleOverrideConfig: " + config);
 
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 if (!updateConfigurationLocked(config)) {
                     throw new CommandException("override-config failed.  See logcat for details.");
                 }
@@ -5081,7 +5081,7 @@
         private void handleResetConfig() {
             Slog.i(TAG, "cmd: handleResetConfig");
 
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 loadConfigurationLocked();
             }
         }
@@ -5090,7 +5090,7 @@
         // should query this information directly from RoleManager instead. Keeping the old behavior
         // by returning the result from package manager.
         private void handleGetDefaultLauncher() throws CommandException {
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 parseOptionsLocked(/* takeUser =*/ true);
 
                 final String defaultLauncher = getDefaultLauncher(mUserId);
@@ -5114,7 +5114,7 @@
         }
 
         private void handleUnloadUser() throws CommandException {
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 parseOptionsLocked(/* takeUser =*/ true);
 
                 Slog.i(TAG, "cmd: handleUnloadUser: user=" + mUserId);
@@ -5124,7 +5124,7 @@
         }
 
         private void handleClearShortcuts() throws CommandException {
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 parseOptionsLocked(/* takeUser =*/ true);
                 final String packageName = getNextArgRequired();
 
@@ -5136,7 +5136,7 @@
         }
 
         private void handleGetShortcuts() throws CommandException {
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 parseOptionsLocked(/* takeUser =*/ true);
                 final String packageName = getNextArgRequired();
 
@@ -5162,7 +5162,7 @@
         }
 
         private void handleHasShortcutAccess() throws CommandException {
-            synchronized (mLock) {
+            synchronized (mServiceLock) {
                 parseOptionsLocked(/* takeUser =*/ true);
                 final String packageName = getNextArgRequired();
 
@@ -5318,7 +5318,7 @@
 
     @VisibleForTesting
     ShortcutPackage getPackageShortcutForTest(String packageName, int userId) {
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             final ShortcutUser user = mUsers.get(userId);
             if (user == null) return null;
 
@@ -5328,7 +5328,7 @@
 
     @VisibleForTesting
     ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId);
             if (pkg == null) return null;
 
@@ -5339,7 +5339,7 @@
     @VisibleForTesting
     void updatePackageShortcutForTest(String packageName, String shortcutId, int userId,
             Consumer<ShortcutInfo> cb) {
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId);
             if (pkg == null) return;
             cb.accept(pkg.findShortcutById(shortcutId));
@@ -5348,7 +5348,7 @@
 
     @VisibleForTesting
     ShortcutLauncher getLauncherShortcutForTest(String packageName, int userId) {
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             final ShortcutUser user = mUsers.get(userId);
             if (user == null) return null;
 
@@ -5385,14 +5385,14 @@
     }
 
     private void verifyStatesInner() {
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates));
         }
     }
 
     @VisibleForTesting
     void waitForBitmapSavesForTest() {
-        synchronized (mLock) {
+        synchronized (mServiceLock) {
             forEachLoadedUserLocked(u ->
                     u.forAllPackageItems(ShortcutPackageItem::waitForBitmapSaves));
         }
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 98499417..deaa8d8 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -494,6 +494,7 @@
         PhoneCarrierPrivilegesCallback(int phoneId) {
             mPhoneId = phoneId;
         }
+
         @Override
         public void onCarrierPrivilegesChanged(
                 @NonNull Set<String> privilegedPackageNames,
@@ -563,7 +564,11 @@
 
         final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
         t.traceBegin("Permission_grant_default_permissions-" + userId);
-        grantOrUpgradeDefaultRuntimePermissionsIfNeeded(userId);
+        if (mPackageManagerInternal.isPermissionUpgradeNeeded(userId)) {
+            grantOrUpgradeDefaultRuntimePermissions(userId);
+            updateUserSensitive(userId);
+            mPackageManagerInternal.updateRuntimePermissionsFingerprint(userId);
+        }
         t.traceEnd();
 
         final OnInitializedCallback callback;
@@ -595,59 +600,56 @@
         }
     }
 
-    private void grantOrUpgradeDefaultRuntimePermissionsIfNeeded(@UserIdInt int userId) {
+    private void grantOrUpgradeDefaultRuntimePermissions(@UserIdInt int userId) {
         if (PermissionManager.USE_ACCESS_CHECKING_SERVICE) {
             return;
         }
 
-        if (DEBUG) Slog.i(LOG_TAG, "grantOrUpgradeDefaultPermsIfNeeded(" + userId + ")");
+        if (DEBUG) Slog.i(LOG_TAG, "grantOrUpgradeDefaultPerms(" + userId + ")");
         final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
 
-        final PackageManagerInternal packageManagerInternal =
-                LocalServices.getService(PackageManagerInternal.class);
-        final PermissionManagerServiceInternal permissionManagerInternal =
-                LocalServices.getService(PermissionManagerServiceInternal.class);
-        if (packageManagerInternal.isPermissionUpgradeNeeded(userId)) {
-            if (DEBUG) Slog.i(LOG_TAG, "defaultPermsWereGrantedSinceBoot(" + userId + ")");
+        // Now call into the permission controller to apply policy around permissions
+        final AndroidFuture<Boolean> future = new AndroidFuture<>();
 
-            // Now call into the permission controller to apply policy around permissions
-            final AndroidFuture<Boolean> future = new AndroidFuture<>();
-
-            // We need to create a local manager that does not schedule work on the main
-            // there as we are on the main thread and want to block until the work is
-            // completed or we time out.
-            final PermissionControllerManager permissionControllerManager =
-                    new PermissionControllerManager(
-                            getUserContext(getContext(), UserHandle.of(userId)),
-                            PermissionThread.getHandler());
-            permissionControllerManager.grantOrUpgradeDefaultRuntimePermissions(
-                    PermissionThread.getExecutor(), successful -> {
-                        if (successful) {
-                            future.complete(null);
-                        } else {
-                            // We are in an undefined state now, let us crash and have
-                            // rescue party suggest a wipe to recover to a good one.
-                            final String message = "Error granting/upgrading runtime permissions"
-                                    + " for user " + userId;
-                            Slog.wtf(LOG_TAG, message);
-                            future.completeExceptionally(new IllegalStateException(message));
-                        }
-                    });
-            try {
-                t.traceBegin("Permission_callback_waiting-" + userId);
-                future.get();
-            } catch (InterruptedException | ExecutionException e) {
-                throw new IllegalStateException(e);
-            } finally {
-                t.traceEnd();
-            }
-
-            permissionControllerManager.updateUserSensitive();
-
-            packageManagerInternal.updateRuntimePermissionsFingerprint(userId);
+        // We need to create a local manager that does not schedule work on the main
+        // there as we are on the main thread and want to block until the work is
+        // completed or we time out.
+        final PermissionControllerManager permissionControllerManager =
+                new PermissionControllerManager(
+                        getUserContext(getContext(), UserHandle.of(userId)),
+                        PermissionThread.getHandler());
+        permissionControllerManager.grantOrUpgradeDefaultRuntimePermissions(
+                PermissionThread.getExecutor(), successful -> {
+                    if (successful) {
+                        future.complete(null);
+                    } else {
+                        // We are in an undefined state now, let us crash and have
+                        // rescue party suggest a wipe to recover to a good one.
+                        final String message = "Error granting/upgrading runtime permissions"
+                                + " for user " + userId;
+                        Slog.wtf(LOG_TAG, message);
+                        future.completeExceptionally(new IllegalStateException(message));
+                    }
+                });
+        try {
+            t.traceBegin("Permission_callback_waiting-" + userId);
+            future.get();
+        } catch (InterruptedException | ExecutionException e) {
+            throw new IllegalStateException(e);
+        } finally {
+            t.traceEnd();
         }
     }
 
+    private void updateUserSensitive(@UserIdInt int userId) {
+        if (DEBUG) Slog.i(LOG_TAG, "updateUserSensitive(" + userId + ")");
+        final PermissionControllerManager permissionControllerManager =
+                new PermissionControllerManager(
+                        getUserContext(getContext(), UserHandle.of(userId)),
+                        PermissionThread.getHandler());
+        permissionControllerManager.updateUserSensitive();
+    }
+
     private static @Nullable Context getUserContext(@NonNull Context context,
             @Nullable UserHandle user) {
         if (context.getUser().equals(user)) {
@@ -695,12 +697,10 @@
         if (DEBUG) Slog.i(LOG_TAG, "synchronizePermissionsAndAppOpsForUser(" + userId + ")");
         final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
 
-        final PackageManagerInternal packageManagerInternal = LocalServices.getService(
-                PackageManagerInternal.class);
         final PermissionToOpSynchroniser synchronizer = new PermissionToOpSynchroniser(
                 getUserContext(getContext(), UserHandle.of(userId)));
         t.traceBegin("Permission_synchronize_addPackages-" + userId);
-        packageManagerInternal.forEachPackage(
+        mPackageManagerInternal.forEachPackage(
                 (pkg) -> synchronizer.addPackage(pkg.getPackageName()));
         t.traceEnd();
         t.traceBegin("Permission_syncPackages-" + userId);
@@ -1052,13 +1052,11 @@
          * @param pkgName The package to add for later processing.
          */
         void addPackage(@NonNull String pkgName) {
-            PackageManagerInternal pmInternal =
-                    LocalServices.getService(PackageManagerInternal.class);
             final PackageInfo pkgInfo;
             final AndroidPackage pkg;
             try {
                 pkgInfo = mPackageManager.getPackageInfo(pkgName, GET_PERMISSIONS);
-                pkg = pmInternal.getPackage(pkgName);
+                pkg = mPackageManagerInternal.getPackage(pkgName);
             } catch (NameNotFoundException e) {
                 return;
             }
diff --git a/services/core/java/com/android/server/policy/SideFpsEventHandler.java b/services/core/java/com/android/server/policy/SideFpsEventHandler.java
index 2d76c50..4ad4353 100644
--- a/services/core/java/com/android/server/policy/SideFpsEventHandler.java
+++ b/services/core/java/com/android/server/policy/SideFpsEventHandler.java
@@ -38,6 +38,7 @@
 import android.view.View;
 import android.view.Window;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -66,6 +67,7 @@
     private final int mDismissDialogTimeout;
     @Nullable
     private SideFpsToast mDialog;
+    private final AccessibilityManager mAccessibilityManager;
     private final Runnable mTurnOffDialog =
             () -> {
                 dismissDialog("mTurnOffDialog");
@@ -96,6 +98,7 @@
             DialogProvider provider) {
         mContext = context;
         mHandler = handler;
+        mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
         mPowerManager = powerManager;
         mBiometricState = STATE_IDLE;
         mSideFpsEventHandlerReady = new AtomicBoolean(false);
@@ -157,7 +160,9 @@
                                 mHandler.removeCallbacks(mTurnOffDialog);
                             }
                             showDialog(eventTime, "Enroll Power Press");
-                            mHandler.postDelayed(mTurnOffDialog, mDismissDialogTimeout);
+                            if (!mAccessibilityManager.isEnabled()) {
+                                mHandler.postDelayed(mTurnOffDialog, mDismissDialogTimeout);
+                            }
                         });
                 return true;
             case STATE_BP_AUTH:
@@ -231,6 +236,10 @@
                                         public void onBiometricAction(
                                                 @BiometricStateListener.Action int action) {
                                             Log.d(TAG, "onBiometricAction " + action);
+                                            if (mAccessibilityManager != null
+                                                    && mAccessibilityManager.isEnabled()) {
+                                                dismissDialog("mTurnOffDialog");
+                                            }
                                         }
                                     });
                             mSideFpsEventHandlerReady.set(true);
@@ -256,6 +265,9 @@
         mLastPowerPressTime = time;
         mDialog.show();
         mDialog.setOnClickListener(this);
+        if (mAccessibilityManager.isEnabled()) {
+            mDialog.addAccessibilityDelegate();
+        }
     }
 
     interface DialogProvider {
diff --git a/services/core/java/com/android/server/policy/SideFpsToast.java b/services/core/java/com/android/server/policy/SideFpsToast.java
index db07467..c27753c 100644
--- a/services/core/java/com/android/server/policy/SideFpsToast.java
+++ b/services/core/java/com/android/server/policy/SideFpsToast.java
@@ -16,6 +16,7 @@
 
 package com.android.server.policy;
 
+import android.annotation.NonNull;
 import android.app.Dialog;
 import android.content.Context;
 import android.os.Bundle;
@@ -23,6 +24,7 @@
 import android.view.View;
 import android.view.Window;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.Button;
 
 import com.android.internal.R;
@@ -34,7 +36,6 @@
  * This dialog is used by {@link SideFpsEventHandler}
  */
 public class SideFpsToast extends Dialog {
-
     SideFpsToast(Context context) {
         super(context);
     }
@@ -66,4 +67,27 @@
             turnOffScreen.setOnClickListener(listener);
         }
     }
+
+    /**
+     * When accessibility mode is on, add AccessibilityDelegate to dismiss dialog when focus is
+     * moved away from the dialog.
+     */
+    public void addAccessibilityDelegate() {
+        final Button turnOffScreen = findViewById(R.id.turn_off_screen);
+        if (turnOffScreen != null) {
+            turnOffScreen.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+                @Override
+                public void onInitializeAccessibilityEvent(@NonNull View host,
+                        @NonNull AccessibilityEvent event) {
+                    if (event.getEventType()
+                            == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
+                            && isShowing()) {
+                        dismiss();
+                    }
+                    super.onInitializeAccessibilityEvent(host, event);
+                }
+            });
+
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index bbb59ce..76cedd8 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -444,6 +444,9 @@
     // Refer to autosuspend.h.
     private boolean mHalAutoSuspendModeEnabled;
 
+    // True if the device uses auto-suspend mode.
+    private final boolean mUseAutoSuspend;
+
     // True if interactive mode is enabled.
     // Refer to power.h.
     private boolean mHalInteractiveModeEnabled;
@@ -1203,6 +1206,9 @@
 
         mPowerGroupWakefulnessChangeListener = new PowerGroupWakefulnessChangeListener();
 
+        mUseAutoSuspend = mContext.getResources().getBoolean(com.android.internal.R.bool
+                .config_useAutoSuspend);
+
         // Save brightness values:
         // Get float values from config.
         // Store float if valid
@@ -3918,6 +3924,9 @@
 
     @GuardedBy("mLock")
     private void setHalAutoSuspendModeLocked(boolean enable) {
+        if (!mUseAutoSuspend) {
+            return;
+        }
         if (enable != mHalAutoSuspendModeEnabled) {
             if (DEBUG) {
                 Slog.d(TAG, "Setting HAL auto-suspend mode to " + enable);
@@ -4661,6 +4670,7 @@
                 pw.println("  mEnhancedDischargePredictionIsPersonalized="
                         + mEnhancedDischargePredictionIsPersonalized);
             }
+            pw.println("  mUseAutoSuspend=" + mUseAutoSuspend);
             pw.println("  mHalAutoSuspendModeEnabled=" + mHalAutoSuspendModeEnabled);
             pw.println("  mHalInteractiveModeEnabled=" + mHalInteractiveModeEnabled);
             pw.println("  mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary));
diff --git a/services/core/java/com/android/server/utils/AnrTimer.java b/services/core/java/com/android/server/utils/AnrTimer.java
index 592d039..12db21d 100644
--- a/services/core/java/com/android/server/utils/AnrTimer.java
+++ b/services/core/java/com/android/server/utils/AnrTimer.java
@@ -40,6 +40,7 @@
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.Objects;
 
 /**
@@ -211,10 +212,6 @@
     @GuardedBy("mLock")
     private int mTotalStarted = 0;
 
-    /** The total number of timers that were restarted without an explicit cancel. */
-    @GuardedBy("mLock")
-    private int mTotalRestarted = 0;
-
     /** The total number of errors detected. */
     @GuardedBy("mLock")
     private int mTotalErrors = 0;
@@ -368,7 +365,7 @@
 
         abstract boolean enabled();
 
-        abstract void dump(PrintWriter pw, boolean verbose);
+        abstract void dump(IndentingPrintWriter pw, boolean verbose);
 
         abstract void close();
     }
@@ -410,9 +407,14 @@
             return false;
         }
 
-        /** dump() is a no-op when the feature is disabled. */
+        /** Dump the limited statistics captured when the feature is disabled. */
         @Override
-        void dump(PrintWriter pw, boolean verbose) {
+        void dump(IndentingPrintWriter pw, boolean verbose) {
+            synchronized (mLock) {
+                pw.format("started=%d maxStarted=%d running=%d expired=%d errors=%d\n",
+                        mTotalStarted, mMaxStarted, mTimerIdMap.size(),
+                        mTotalExpired, mTotalErrors);
+            }
         }
 
         /** close() is a no-op when the feature is disabled. */
@@ -441,6 +443,10 @@
          */
         private long mNative = 0;
 
+        /** The total number of timers that were restarted without an explicit cancel. */
+        @GuardedBy("mLock")
+        private int mTotalRestarted = 0;
+
         /** Fetch the native tag (an integer) for the given label. */
         FeatureEnabled() {
             mNative = nativeAnrTimerCreate(mLabel);
@@ -537,13 +543,22 @@
 
         /** Dump statistics from the native layer. */
         @Override
-        void dump(PrintWriter pw, boolean verbose) {
+        void dump(IndentingPrintWriter pw, boolean verbose) {
             synchronized (mLock) {
-                if (mNative != 0) {
-                    nativeAnrTimerDump(mNative, verbose);
-                } else {
+                if (mNative == 0) {
                     pw.println("closed");
+                    return;
                 }
+                String[] nativeDump = nativeAnrTimerDump(mNative);
+                if (nativeDump == null) {
+                    pw.println("no-data");
+                    return;
+                }
+                for (String s : nativeDump) {
+                    pw.println(s);
+                }
+                // The following counter is only available at the Java level.
+                pw.println("restarted:" + mTotalRestarted);
             }
         }
 
@@ -690,11 +705,8 @@
         synchronized (mLock) {
             pw.format("timer: %s\n", mLabel);
             pw.increaseIndent();
-            pw.format("started=%d maxStarted=%d  restarted=%d running=%d expired=%d errors=%d\n",
-                    mTotalStarted, mMaxStarted, mTotalRestarted, mTimerIdMap.size(),
-                    mTotalExpired, mTotalErrors);
-            pw.decreaseIndent();
             mFeature.dump(pw, false);
+            pw.decreaseIndent();
         }
     }
 
@@ -755,6 +767,14 @@
         recordErrorLocked(operation, "notFound", arg);
     }
 
+    /** Compare two AnrTimers in display order. */
+    private static final Comparator<AnrTimer> sComparator =
+            Comparator.nullsLast(new Comparator<>() {
+                    @Override
+                    public int compare(AnrTimer o1, AnrTimer o2) {
+                        return o1.mLabel.compareTo(o2.mLabel);
+                    }});
+
     /** Dumpsys output, allowing for overrides. */
     @VisibleForTesting
     static void dump(@NonNull PrintWriter pw, boolean verbose, @NonNull Injector injector) {
@@ -764,11 +784,18 @@
         ipw.println("AnrTimer statistics");
         ipw.increaseIndent();
         synchronized (sAnrTimerList) {
+            // Find the currently live instances and sort them by their label.  The goal is to
+            // have consistent output ordering.
             final int size = sAnrTimerList.size();
-            ipw.println("reporting " + size + " timers");
+            AnrTimer[] active = new AnrTimer[size];
+            int valid = 0;
             for (int i = 0; i < size; i++) {
                 AnrTimer a = sAnrTimerList.valueAt(i).get();
-                if (a != null) a.dump(ipw);
+                if (a != null) active[valid++] = a;
+            }
+            Arrays.sort(active, 0, valid, sComparator);
+            for (int i = 0; i < valid; i++) {
+                if (active[i] != null) active[i].dump(ipw);
             }
         }
         if (verbose) dumpErrors(ipw);
@@ -827,6 +854,6 @@
     /** Discard an expired timer by ID.  Return true if the timer was found.  */
     private static native boolean nativeAnrTimerDiscard(long service, int timerId);
 
-    /** Prod the native library to log a few statistics. */
-    private static native void nativeAnrTimerDump(long service, boolean verbose);
+    /** Retrieve runtime dump information from the native layer. */
+    private static native String[] nativeAnrTimerDump(long service);
 }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1f320da..7d057a9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -9817,10 +9817,10 @@
         if (mLetterboxUiController.shouldApplyUserMinAspectRatioOverride()) {
             return mLetterboxUiController.getUserMinAspectRatio();
         }
-        if (!mLetterboxUiController.shouldOverrideMinAspectRatio()) {
+        if (!mLetterboxUiController.shouldOverrideMinAspectRatio()
+                && !mLetterboxUiController.shouldOverrideMinAspectRatioForCamera()) {
             return info.getMinAspectRatio();
         }
-
         if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
                 && !ActivityInfo.isFixedOrientationPortrait(
                         getOverrideOrientation())) {
@@ -9941,6 +9941,16 @@
         return updateReportedConfigurationAndSend();
     }
 
+    /**
+     * @return {@code true} if the Camera is active for the current activity
+     */
+    boolean isCameraActive() {
+        return mDisplayContent != null
+                && mDisplayContent.getDisplayRotationCompatPolicy() != null
+                && mDisplayContent.getDisplayRotationCompatPolicy()
+                    .isCameraActive(this, /* mustBeFullscreen */ true);
+    }
+
     boolean updateReportedConfigurationAndSend() {
         if (isConfigurationDispatchPaused()) {
             Slog.wtf(TAG, "trying to update reported(client) config while dispatch is paused");
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f7e5dd8..2f37e88 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1318,6 +1318,15 @@
         }
     }
 
+    /**
+     * @return The {@link DisplayRotationCompatPolicy} for this DisplayContent
+     */
+    // TODO(b/335387481) Allow access to DisplayRotationCompatPolicy only with getters
+    @Nullable
+    DisplayRotationCompatPolicy getDisplayRotationCompatPolicy() {
+        return mDisplayRotationCompatPolicy;
+    }
+
     @Override
     void migrateToNewSurfaceControl(Transaction t) {
         t.remove(mSurfaceControl);
@@ -2021,12 +2030,11 @@
         }
         // Update directly because the app which will change the orientation of display is ready.
         if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) {
-            // Run rotation change on display thread. See Transition#shouldApplyOnDisplayThread().
-            mWmService.mH.post(() -> {
-                synchronized (mWmService.mGlobalLock) {
-                    sendNewConfiguration();
-                }
-            });
+            // If a transition is collecting, let the transition apply the rotation change on
+            // display thread. See Transition#shouldApplyOnDisplayThread().
+            if (!mTransitionController.isCollecting(this)) {
+                sendNewConfiguration();
+            }
             return;
         }
         if (mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange()) {
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index d5f8df3..eacf9a3 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -64,7 +64,7 @@
  * R.bool.config_isWindowManagerCameraCompatTreatmentEnabled} is {@code true}.
  */
  // TODO(b/261444714): Consider moving Camera-specific logic outside of the WM Core path
-final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraCompatStateListener {
+class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraCompatStateListener {
 
     // Delay for updating display rotation after Camera connection is closed. Needed to avoid
     // rotation flickering when an app is flipping between front and rear cameras or when size
@@ -306,7 +306,8 @@
 
     boolean isActivityEligibleForOrientationOverride(@NonNull ActivityRecord activity) {
         return isTreatmentEnabledForDisplay()
-                && isCameraActive(activity, /* mustBeFullscreen */ true);
+                && isCameraActive(activity, /* mustBeFullscreen */ true)
+                && activity.mLetterboxUiController.shouldForceRotateForCameraCompat();
     }
 
 
@@ -324,6 +325,13 @@
         return isTreatmentEnabledForActivity(activity, /* mustBeFullscreen */ true);
     }
 
+    boolean isCameraActive(@NonNull ActivityRecord activity, boolean mustBeFullscreen) {
+        // Checking windowing mode on activity level because we don't want to
+        // apply treatment in case of activity embedding.
+        return (!mustBeFullscreen || !activity.inMultiWindowMode())
+                && mCameraStateMonitor.isCameraRunningForActivity(activity);
+    }
+
     private boolean isTreatmentEnabledForActivity(@Nullable ActivityRecord activity,
             boolean mustBeFullscreen) {
         return activity != null && isCameraActive(activity, mustBeFullscreen)
@@ -331,14 +339,7 @@
                 // "locked" and "nosensor" values are often used by camera apps that can't
                 // handle dynamic changes so we shouldn't force rotate them.
                 && activity.getOverrideOrientation() != SCREEN_ORIENTATION_NOSENSOR
-                && activity.getOverrideOrientation() != SCREEN_ORIENTATION_LOCKED;
-    }
-
-    private boolean isCameraActive(@NonNull ActivityRecord activity, boolean mustBeFullscreen) {
-        // Checking windowing mode on activity level because we don't want to
-        // apply treatment in case of activity embedding.
-        return (!mustBeFullscreen || !activity.inMultiWindowMode())
-                && mCameraStateMonitor.isCameraRunningForActivity(activity)
+                && activity.getOverrideOrientation() != SCREEN_ORIENTATION_LOCKED
                 && activity.mLetterboxUiController.shouldForceRotateForCameraCompat();
     }
 
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 683efde..b38e666 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -30,6 +30,7 @@
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
 import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
 import static android.content.pm.ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
@@ -510,6 +511,26 @@
     }
 
     /**
+     * Whether we should apply the min aspect ratio per-app override only when an app is connected
+     * to the camera.
+     * When this override is applied the min aspect ratio given in the app's manifest will be
+     * overridden to the largest enabled aspect ratio treatment unless the app's manifest value
+     * is higher. The treatment will also apply if no value is provided in the manifest.
+     *
+     * <p>This method returns {@code true} when the following conditions are met:
+     * <ul>
+     *     <li>Opt-out component property isn't enabled
+     *     <li>Per-app override is enabled
+     * </ul>
+     */
+    boolean shouldOverrideMinAspectRatioForCamera() {
+        return mActivityRecord.isCameraActive()
+                && mAllowMinAspectRatioOverrideOptProp
+                .shouldEnableWithOptInOverrideAndOptOutProperty(
+                        isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA));
+    }
+
+    /**
      * Whether we should apply the force resize per-app override. When this override is applied it
      * forces the packages it is applied to to be resizable. It won't change whether the app can be
      * put into multi-windowing mode, but allow the app to resize without going into size-compat
@@ -962,7 +983,8 @@
 
     void recomputeConfigurationForCameraCompatIfNeeded() {
         if (isOverrideOrientationOnlyForCameraEnabled()
-                || isCameraCompatSplitScreenAspectRatioAllowed()) {
+                || isCameraCompatSplitScreenAspectRatioAllowed()
+                || shouldOverrideMinAspectRatioForCamera()) {
             mActivityRecord.recomputeConfiguration();
         }
     }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9b98380..301d0a5 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -43,7 +43,6 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -641,8 +640,6 @@
         mLastTaskSnapshotData = _lastSnapshotData != null
                 ? _lastSnapshotData
                 : new PersistedTaskSnapshotData();
-        // Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
-        setOrientation(SCREEN_ORIENTATION_UNSET);
         affinityIntent = _affinityIntent;
         affinity = _affinity;
         rootAffinity = _rootAffinity;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index a437914..cb8304c 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -186,7 +186,7 @@
     // The specified orientation for this window container.
     // Shouldn't be accessed directly since subclasses can override getOverrideOrientation.
     @ScreenOrientation
-    private int mOverrideOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+    private int mOverrideOrientation = SCREEN_ORIENTATION_UNSET;
 
     /**
      * The window container which decides its orientation since the last time
@@ -1683,8 +1683,6 @@
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer wc = mChildren.get(i);
 
-            // TODO: Maybe mOverrideOrientation should default to SCREEN_ORIENTATION_UNSET vs.
-            // SCREEN_ORIENTATION_UNSPECIFIED?
             final int orientation = wc.getOrientation(candidate == SCREEN_ORIENTATION_BEHIND
                     ? SCREEN_ORIENTATION_BEHIND : SCREEN_ORIENTATION_UNSET);
             if (orientation == SCREEN_ORIENTATION_BEHIND) {
@@ -1700,7 +1698,7 @@
                 continue;
             }
 
-            if (wc.providesOrientation() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+            if (orientation != SCREEN_ORIENTATION_UNSPECIFIED || wc.providesOrientation()) {
                 // Use the orientation if the container can provide or requested an explicit
                 // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED.
                 ProtoLog.v(WM_DEBUG_ORIENTATION, "%s is requesting orientation %d (%s)",
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 25c5db1..64cbb0d1 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -12,6 +12,7 @@
 per-file com_android_server_Usb* = file:/services/usb/OWNERS
 per-file com_android_server_Vibrator* = file:/services/core/java/com/android/server/vibrator/OWNERS
 per-file com_android_server_accessibility_* = file:/services/accessibility/OWNERS
+per-file com_android_server_adb_* = file:/services/core/java/com/android/server/adb/OWNERS
 per-file com_android_server_display_* = file:/services/core/java/com/android/server/display/OWNERS
 per-file com_android_server_hdmi_* = file:/core/java/android/hardware/hdmi/OWNERS
 per-file com_android_server_lights_* = file:/services/core/java/com/android/server/lights/OWNERS
diff --git a/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp b/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp
index 9c834aa..c7b6852 100644
--- a/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp
+++ b/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp
@@ -18,58 +18,22 @@
 
 #define LOG_NDEBUG 0
 
-#include <algorithm>
 #include <condition_variable>
 #include <mutex>
 #include <optional>
-#include <random>
-#include <string>
-#include <vector>
 
 #include <adb/pairing/pairing_server.h>
 #include <android-base/properties.h>
-#include <utils/Log.h>
-
+#include <jni.h>
 #include <nativehelper/JNIHelp.h>
-#include "jni.h"
+#include <nativehelper/utils.h>
+#include <utils/Log.h>
 
 namespace android {
 
 // ----------------------------------------------------------------------------
 namespace {
 
-template <class T, class N>
-class JSmartWrapper {
-public:
-    JSmartWrapper(JNIEnv* env, T* jData) : mEnv(env), mJData(jData) {}
-
-    virtual ~JSmartWrapper() = default;
-
-    const N* data() const { return mRawData; }
-
-    jsize size() const { return mSize; }
-
-protected:
-    N* mRawData = nullptr;
-    JNIEnv* mEnv = nullptr;
-    T* mJData = nullptr;
-    jsize mSize = 0;
-}; // JSmartWrapper
-
-class JStringUTFWrapper : public JSmartWrapper<jstring, const char> {
-public:
-    explicit JStringUTFWrapper(JNIEnv* env, jstring* str) : JSmartWrapper(env, str) {
-        mRawData = env->GetStringUTFChars(*str, NULL);
-        mSize = env->GetStringUTFLength(*str);
-    }
-
-    virtual ~JStringUTFWrapper() {
-        if (data()) {
-            mEnv->ReleaseStringUTFChars(*mJData, mRawData);
-        }
-    }
-}; // JStringUTFWrapper
-
 struct ServerDeleter {
     void operator()(PairingServerCtx* p) { pairing_server_destroy(p); }
 };
@@ -97,19 +61,19 @@
 std::unique_ptr<PairingResultWaiter> sWaiter;
 } // namespace
 
-static jint native_pairing_start(JNIEnv* env, jobject thiz, jstring guid, jstring password) {
+static jint native_pairing_start(JNIEnv* env, jobject thiz, jstring javaGuid, jstring javaPassword) {
     // Server-side only sends its GUID on success.
-    PeerInfo system_info = {};
-    system_info.type = ADB_DEVICE_GUID;
-    JStringUTFWrapper guidWrapper(env, &guid);
-    memcpy(system_info.data, guidWrapper.data(), guidWrapper.size());
+    PeerInfo system_info = { .type = ADB_DEVICE_GUID };
 
-    JStringUTFWrapper passwordWrapper(env, &password);
+    ScopedUtfChars guid = GET_UTF_OR_RETURN(env, javaGuid);
+    memcpy(system_info.data, guid.c_str(), guid.size());
+
+    ScopedUtfChars password = GET_UTF_OR_RETURN(env, javaPassword);
 
     // Create the pairing server
     sServer = PairingServerPtr(
-            pairing_server_new_no_cert(reinterpret_cast<const uint8_t*>(passwordWrapper.data()),
-                                       passwordWrapper.size(), &system_info, 0));
+            pairing_server_new_no_cert(reinterpret_cast<const uint8_t*>(password.c_str()),
+                                       password.size(), &system_info, 0));
 
     sWaiter.reset(new PairingResultWaiter);
     uint16_t port = pairing_server_start(sServer.get(), sWaiter->ResultCallback, sWaiter.get());
@@ -137,11 +101,16 @@
         return JNI_FALSE;
     }
 
-    std::string peer_public_key = reinterpret_cast<char*>(sWaiter->peer_info_.data);
-    // Write to PairingThread's member variables
+    // Create a Java string for the public key.
+    char* peer_public_key = reinterpret_cast<char*>(sWaiter->peer_info_.data);
+    jstring jpublickey = env->NewStringUTF(peer_public_key);
+    if (jpublickey == nullptr) {
+      return JNI_FALSE;
+    }
+
+    // Write to PairingThread.mPublicKey.
     jclass clazz = env->GetObjectClass(thiz);
     jfieldID mPublicKey = env->GetFieldID(clazz, "mPublicKey", "Ljava/lang/String;");
-    jstring jpublickey = env->NewStringUTF(peer_public_key.c_str());
     env->SetObjectField(thiz, mPublicKey, jpublickey);
     return JNI_TRUE;
 }
@@ -157,12 +126,9 @@
 };
 
 int register_android_server_AdbDebuggingManager(JNIEnv* env) {
-    int res = jniRegisterNativeMethods(env,
-                                       "com/android/server/adb/AdbDebuggingManager$PairingThread",
-                                       gPairingThreadMethods, NELEM(gPairingThreadMethods));
-    (void)res; // Faked use when LOG_NDEBUG.
-    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
-    return 0;
+    return jniRegisterNativeMethods(env,
+                                    "com/android/server/adb/AdbDebuggingManager$PairingThread",
+                                    gPairingThreadMethods, NELEM(gPairingThreadMethods));
 }
 
 } /* namespace android */
diff --git a/services/core/jni/com_android_server_utils_AnrTimer.cpp b/services/core/jni/com_android_server_utils_AnrTimer.cpp
index da95666..6509958 100644
--- a/services/core/jni/com_android_server_utils_AnrTimer.cpp
+++ b/services/core/jni/com_android_server_utils_AnrTimer.cpp
@@ -161,14 +161,14 @@
     // A timer has expired.
     void expire(timer_id_t);
 
-    // Dump a small amount of state to the log file.
-    void dump(bool verbose) const;
-
     // Return the Java object associated with this instance.
     jweak jtimer() const {
         return notifierObject_;
     }
 
+    // Return the per-instance statistics.
+    std::vector<std::string> getDump() const;
+
   private:
     // The service cannot be copied.
     AnrTimerService(AnrTimerService const &) = delete;
@@ -199,7 +199,7 @@
     std::set<Timer> running_;
 
     // The maximum number of active timers.
-    size_t maxActive_;
+    size_t maxRunning_;
 
     // Simple counters
     struct Counters {
@@ -209,6 +209,7 @@
         size_t accepted;
         size_t discarded;
         size_t expired;
+        size_t extended;
 
         // The number of times there were zero active timers.
         size_t drained;
@@ -437,7 +438,9 @@
 
     // Construct the ticker.  This creates the timerfd file descriptor and starts the monitor
     // thread.  The monitor thread is given a unique name.
-    Ticker() {
+    Ticker() :
+            id_(idGen_.fetch_add(1))
+    {
         timerFd_ = timer_create();
         if (timerFd_ < 0) {
             ALOGE("failed to create timerFd: %s", strerror(errno));
@@ -502,6 +505,11 @@
         }
     }
 
+    // The unique ID of this particular ticker. Used for debug and logging.
+    size_t id() const {
+        return id_;
+    }
+
     // Return the number of timers still running.
     size_t running() const {
         AutoMutex _l(lock_);
@@ -617,8 +625,16 @@
     // The list of timers that are scheduled.  This set is sorted by timeout and then by timer
     // ID.  A set is sufficient (as opposed to a multiset) because timer IDs are unique.
     std::set<Entry> running_;
+
+    // A unique ID assigned to this instance.
+    const size_t id_;
+
+    // The ID generator.
+    static std::atomic<size_t> idGen_;
 };
 
+std::atomic<size_t> AnrTimerService::Ticker::idGen_;
+
 
 AnrTimerService::AnrTimerService(char const* label,
             notifier_t notifier, void* cookie, jweak jtimer, Ticker* ticker) :
@@ -629,7 +645,7 @@
         ticker_(ticker) {
 
     // Zero the statistics
-    maxActive_ = 0;
+    maxRunning_ = 0;
     memset(&counters_, 0, sizeof(counters_));
 
     ALOGI_IF(DEBUG, "initialized %s", label);
@@ -739,6 +755,12 @@
         elapsed = now() - t.started;
     }
 
+    if (expired) {
+        counters_.expired++;
+    } else {
+        counters_.extended++;
+    }
+
     // Deliver the notification outside of the lock.
     if (expired) {
         if (!notifier_(timerId, pid, uid, elapsed, notifierCookie_, notifierObject_)) {
@@ -756,8 +778,8 @@
     if (t.status == Running) {
         // Only forward running timers to the ticker.  Expired timers are handled separately.
         ticker_->insert(t.scheduled, t.id, this);
-        maxActive_ = std::max(maxActive_, running_.size());
     }
+    maxRunning_ = std::max(maxRunning_, running_.size());
 }
 
 AnrTimerService::Timer AnrTimerService::remove(timer_id_t timerId) {
@@ -767,29 +789,32 @@
         Timer result = *found;
         running_.erase(found);
         ticker_->remove(result.scheduled, result.id);
+        if (running_.size() == 0) counters_.drained++;
         return result;
     }
     return Timer();
 }
 
-void AnrTimerService::dump(bool verbose) const {
+std::vector<std::string> AnrTimerService::getDump() const {
+    std::vector<std::string> r;
     AutoMutex _l(lock_);
-    ALOGI("timer %s ops started=%zu canceled=%zu accepted=%zu discarded=%zu expired=%zu",
-          label_.c_str(),
-          counters_.started, counters_.canceled, counters_.accepted,
-          counters_.discarded, counters_.expired);
-    ALOGI("timer %s stats max-active=%zu/%zu running=%zu/%zu errors=%zu",
-          label_.c_str(),
-          maxActive_, ticker_->maxRunning(), running_.size(), ticker_->running(),
-          counters_.error);
-
-    if (verbose) {
-        nsecs_t time = now();
-        for (auto i = running_.begin(); i != running_.end(); i++) {
-            Timer t = *i;
-            ALOGI("   running %s", t.toString(time).c_str());
-        }
-    }
+    r.push_back(StringPrintf("started:%zu canceled:%zu accepted:%zu discarded:%zu expired:%zu",
+                             counters_.started,
+                             counters_.canceled,
+                             counters_.accepted,
+                             counters_.discarded,
+                             counters_.expired));
+    r.push_back(StringPrintf("extended:%zu drained:%zu error:%zu running:%zu maxRunning:%zu",
+                             counters_.extended,
+                             counters_.drained,
+                             counters_.error,
+                             running_.size(),
+                             maxRunning_));
+    r.push_back(StringPrintf("ticker:%zu ticking:%zu maxTicking:%zu",
+                             ticker_->id(),
+                             ticker_->running(),
+                             ticker_->maxRunning()));
+    return r;
 }
 
 /**
@@ -894,21 +919,26 @@
     return toService(ptr)->discard(timerId);
 }
 
-jint anrTimerDump(JNIEnv *env, jclass, jlong ptr, jboolean verbose) {
-    if (!nativeSupportEnabled) return -1;
-    toService(ptr)->dump(verbose);
-    return 0;
+jobjectArray anrTimerDump(JNIEnv *env, jclass, jlong ptr) {
+    if (!nativeSupportEnabled) return nullptr;
+    std::vector<std::string> stats = toService(ptr)->getDump();
+    jclass sclass = env->FindClass("java/lang/String");
+    jobjectArray r = env->NewObjectArray(stats.size(), sclass, nullptr);
+    for (size_t i = 0; i < stats.size(); i++) {
+        env->SetObjectArrayElement(r, i, env->NewStringUTF(stats[i].c_str()));
+    }
+    return r;
 }
 
 static const JNINativeMethod methods[] = {
     {"nativeAnrTimerSupported", "()Z",  (void*) anrTimerSupported},
-    {"nativeAnrTimerCreate", "(Ljava/lang/String;)J", (void*) anrTimerCreate},
-    {"nativeAnrTimerClose", "(J)I",     (void*) anrTimerClose},
-    {"nativeAnrTimerStart", "(JIIJZ)I", (void*) anrTimerStart},
-    {"nativeAnrTimerCancel", "(JI)Z",   (void*) anrTimerCancel},
-    {"nativeAnrTimerAccept", "(JI)Z",   (void*) anrTimerAccept},
-    {"nativeAnrTimerDiscard", "(JI)Z",  (void*) anrTimerDiscard},
-    {"nativeAnrTimerDump", "(JZ)V",     (void*) anrTimerDump},
+    {"nativeAnrTimerCreate",   "(Ljava/lang/String;)J", (void*) anrTimerCreate},
+    {"nativeAnrTimerClose",    "(J)I",     (void*) anrTimerClose},
+    {"nativeAnrTimerStart",    "(JIIJZ)I", (void*) anrTimerStart},
+    {"nativeAnrTimerCancel",   "(JI)Z",    (void*) anrTimerCancel},
+    {"nativeAnrTimerAccept",   "(JI)Z",    (void*) anrTimerAccept},
+    {"nativeAnrTimerDiscard",  "(JI)Z",    (void*) anrTimerDiscard},
+    {"nativeAnrTimerDump",     "(J)[Ljava/lang/String;", (void*) anrTimerDump},
 };
 
 } // anonymous namespace
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d555f1a..85ab562 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -21610,7 +21610,8 @@
                                 == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
             }
 
-            if (Flags.headlessSingleUserFixes() && isSingleUserMode && !mInjector.isChangeEnabled(
+            if (Flags.headlessSingleUserFixes() && mInjector.userManagerIsHeadlessSystemUserMode()
+                    && isSingleUserMode && !mInjector.isChangeEnabled(
                     PROVISION_SINGLE_USER_MODE, deviceAdmin.getPackageName(), caller.getUserId())) {
                 throw new IllegalStateException("Device admin is not targeting Android V.");
             }
@@ -21629,7 +21630,7 @@
             setLocale(provisioningParams.getLocale());
 
             int deviceOwnerUserId = Flags.headlessDeviceOwnerSingleUserEnabled()
-                    && isSingleUserMode
+                    && isSingleUserMode && mInjector.userManagerIsHeadlessSystemUserMode()
                     ? mUserManagerInternal.getMainUserId() : UserHandle.USER_SYSTEM;
 
             if (!removeNonRequiredAppsForManagedDevice(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
new file mode 100644
index 0000000..f0abcd2
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.server.display.mode
+
+import android.content.Context
+import android.util.SparseArray
+import android.view.Display
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SmallTest
+import com.android.server.display.feature.DisplayManagerFlags
+import com.android.server.display.mode.DisplayModeDirector.DisplayDeviceConfigProvider
+import com.android.server.display.mode.RefreshRateVote.RenderVote
+import com.android.server.testutils.TestHandler
+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
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(TestParameterInjector::class)
+class AppRequestObserverTest {
+
+    private lateinit var context: Context
+    private val mockInjector = mock<DisplayModeDirector.Injector>()
+    private val mockFlags = mock<DisplayManagerFlags>()
+    private val mockDisplayDeviceConfigProvider = mock<DisplayDeviceConfigProvider>()
+    private val testHandler = TestHandler(null)
+
+    @Before
+    fun setUp() {
+        context = ApplicationProvider.getApplicationContext()
+    }
+
+    @Test
+    fun `test app request votes`(@TestParameter testCase: AppRequestTestCase) {
+        whenever(mockFlags.ignoreAppPreferredRefreshRateRequest())
+                .thenReturn(testCase.ignoreRefreshRateRequest)
+        val displayModeDirector = DisplayModeDirector(
+            context, testHandler, mockInjector, mockFlags, mockDisplayDeviceConfigProvider)
+        val modes = arrayOf(
+            Display.Mode(1, 1000, 1000, 60f),
+            Display.Mode(2, 1000, 1000, 90f),
+            Display.Mode(3, 1000, 1000, 120f)
+        )
+        displayModeDirector.injectSupportedModesByDisplay(SparseArray<Array<Display.Mode>>().apply {
+            append(Display.DEFAULT_DISPLAY, modes)
+        })
+        displayModeDirector.injectDefaultModeByDisplay(SparseArray<Display.Mode>().apply {
+            append(Display.DEFAULT_DISPLAY, modes[0])
+        })
+
+        displayModeDirector.appRequestObserver.setAppRequest(Display.DEFAULT_DISPLAY,
+            testCase.modeId,
+            testCase.requestedRefreshRates,
+            testCase.requestedMinRefreshRates,
+            testCase.requestedMaxRefreshRates)
+
+        val baseModeVote = displayModeDirector.getVote(Display.DEFAULT_DISPLAY,
+            Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE)
+        assertThat(baseModeVote).isEqualTo(testCase.expectedBaseModeVote)
+
+        val sizeVote = displayModeDirector.getVote(Display.DEFAULT_DISPLAY,
+            Vote.PRIORITY_APP_REQUEST_SIZE)
+        assertThat(sizeVote).isEqualTo(testCase.expectedSizeVote)
+
+        val renderRateVote = displayModeDirector.getVote(Display.DEFAULT_DISPLAY,
+                Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE)
+        assertThat(renderRateVote).isEqualTo(testCase.expectedRenderRateVote)
+    }
+
+    enum class AppRequestTestCase(
+        val ignoreRefreshRateRequest: Boolean,
+        val modeId: Int,
+        val requestedRefreshRates: Float,
+        val requestedMinRefreshRates: Float,
+        val requestedMaxRefreshRates: Float,
+        internal val expectedBaseModeVote: Vote?,
+        internal val expectedSizeVote: Vote?,
+        internal val expectedRenderRateVote: Vote?,
+    ) {
+        BASE_MODE_60(true, 1, 0f, 0f, 0f,
+            BaseModeRefreshRateVote(60f), SizeVote(1000, 1000, 1000, 1000), null),
+        BASE_MODE_90(true, 2, 0f, 0f, 0f,
+            BaseModeRefreshRateVote(90f), SizeVote(1000, 1000, 1000, 1000), null),
+        MIN_REFRESH_RATE_60(true, 0, 0f, 60f, 0f,
+            null, null, RenderVote(60f, Float.POSITIVE_INFINITY)),
+        MIN_REFRESH_RATE_120(true, 0, 0f, 120f, 0f,
+        null, null, RenderVote(120f, Float.POSITIVE_INFINITY)),
+        MAX_REFRESH_RATE_60(true, 0, 0f, 0f, 60f,
+            null, null, RenderVote(0f, 60f)),
+        MAX_REFRESH_RATE_120(true, 0, 0f, 0f, 120f,
+            null, null, RenderVote(0f, 120f)),
+        INVALID_MIN_MAX_REFRESH_RATE(true, 0, 0f, 90f, 60f,
+        null, null, null),
+        BASE_MODE_MIN_MAX(true, 1, 0f, 60f, 90f,
+            BaseModeRefreshRateVote(60f), SizeVote(1000, 1000, 1000, 1000), RenderVote(60f, 90f)),
+        PREFERRED_REFRESH_RATE(false, 0, 60f, 0f, 0f,
+            BaseModeRefreshRateVote(60f), SizeVote(1000, 1000, 1000, 1000), null),
+        PREFERRED_REFRESH_RATE_IGNORED(true, 0, 60f, 0f, 0f,
+            null, null, null),
+        PREFERRED_REFRESH_RATE_INVALID(false, 0, 45f, 0f, 0f,
+            null, null, null),
+    }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 52e157b..cd1e9e8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -31,7 +31,6 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -2262,162 +2261,6 @@
     }
 
     @Test
-    public void testAppRequestObserver_modeId() {
-        DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
-        director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 0, 0);
-
-        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
-        assertNotNull(vote);
-        assertThat(vote).isInstanceOf(BaseModeRefreshRateVote.class);
-        BaseModeRefreshRateVote baseModeVote = (BaseModeRefreshRateVote) vote;
-        assertThat(baseModeVote.mAppRequestBaseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
-
-        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
-        assertNotNull(vote);
-        assertThat(vote).isInstanceOf(SizeVote.class);
-        SizeVote sizeVote = (SizeVote) vote;
-        assertThat(sizeVote.mHeight).isEqualTo(1000);
-        assertThat(sizeVote.mWidth).isEqualTo(1000);
-        assertThat(sizeVote.mMinHeight).isEqualTo(1000);
-        assertThat(sizeVote.mMinWidth).isEqualTo(1000);
-
-        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNull(vote);
-
-        director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 90, 0, 0);
-
-        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
-        assertNotNull(vote);
-        assertThat(vote).isInstanceOf(BaseModeRefreshRateVote.class);
-        baseModeVote = (BaseModeRefreshRateVote) vote;
-        assertThat(baseModeVote.mAppRequestBaseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
-
-        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
-        assertNotNull(vote);
-        assertThat(vote).isInstanceOf(SizeVote.class);
-        sizeVote = (SizeVote) vote;
-        assertThat(sizeVote.mHeight).isEqualTo(1000);
-        assertThat(sizeVote.mWidth).isEqualTo(1000);
-        assertThat(sizeVote.mMinHeight).isEqualTo(1000);
-        assertThat(sizeVote.mMinWidth).isEqualTo(1000);
-
-        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNull(vote);
-    }
-
-    @Test
-    public void testAppRequestObserver_minRefreshRate() {
-        DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
-        director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 60, 0);
-        Vote appRequestRefreshRate =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
-        assertNull(appRequestRefreshRate);
-
-        Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
-        assertNull(appRequestSize);
-
-        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNotNull(vote);
-        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
-        RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
-        assertThat(renderVote.mMinRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
-        assertThat(renderVote.mMaxRefreshRate).isAtLeast(90);
-
-        director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 90, 0);
-        appRequestRefreshRate =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
-        assertNull(appRequestRefreshRate);
-
-        appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
-        assertNull(appRequestSize);
-
-        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNotNull(vote);
-        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
-        renderVote = (RefreshRateVote.RenderVote) vote;
-        assertThat(renderVote.mMinRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
-        assertThat(renderVote.mMaxRefreshRate).isAtLeast(90);
-    }
-
-    @Test
-    public void testAppRequestObserver_maxRefreshRate() {
-        DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
-        director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 0, 90);
-        Vote appRequestRefreshRate =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
-        assertNull(appRequestRefreshRate);
-
-        Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
-        assertNull(appRequestSize);
-
-        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNotNull(vote);
-        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
-        RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
-        assertThat(renderVote.mMinRefreshRate).isZero();
-        assertThat(renderVote.mMaxRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
-
-        director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 0, 60);
-        appRequestRefreshRate =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
-        assertNull(appRequestRefreshRate);
-
-        appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
-        assertNull(appRequestSize);
-
-        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNotNull(vote);
-        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
-        renderVote = (RefreshRateVote.RenderVote) vote;
-        assertThat(renderVote.mMinRefreshRate).isZero();
-        assertThat(renderVote.mMaxRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
-    }
-
-    @Test
-    public void testAppRequestObserver_invalidRefreshRateRange() {
-        DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
-        director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 90, 60);
-        Vote appRequestRefreshRate =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
-        assertNull(appRequestRefreshRate);
-
-        Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
-        assertNull(appRequestSize);
-
-        Vote appRequestRefreshRateRange =
-                director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNull(appRequestRefreshRateRange);
-    }
-
-    @Test
-    public void testAppRequestObserver_modeIdAndRefreshRateRange() {
-        DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
-        director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 90, 90);
-
-        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
-        assertNotNull(vote);
-        assertThat(vote).isInstanceOf(BaseModeRefreshRateVote.class);
-        BaseModeRefreshRateVote baseModeVote = (BaseModeRefreshRateVote) vote;
-        assertThat(baseModeVote.mAppRequestBaseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
-
-        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
-        assertNotNull(vote);
-        assertThat(vote).isInstanceOf(SizeVote.class);
-        SizeVote sizeVote = (SizeVote) vote;
-        assertThat(sizeVote.mHeight).isEqualTo(1000);
-        assertThat(sizeVote.mWidth).isEqualTo(1000);
-        assertThat(sizeVote.mMinHeight).isEqualTo(1000);
-        assertThat(sizeVote.mMinWidth).isEqualTo(1000);
-
-        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
-        assertNotNull(vote);
-        assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
-        RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
-        assertThat(renderVote.mMinRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
-        assertThat(renderVote.mMaxRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
-    }
-
-    @Test
     public void testAppRequestsIsTheDefaultMode() {
         Display.Mode[] modes = new Display.Mode[2];
         modes[0] = new Display.Mode(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
index 3e0677c..4d910ce 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
@@ -146,8 +146,8 @@
 
         val modes = arrayOf(
             Display.Mode(1, 1000, 1000, 60f),
-            Display.Mode(1, 1000, 1000, 90f),
-            Display.Mode(1, 1000, 1000, 120f)
+            Display.Mode(2, 1000, 1000, 90f),
+            Display.Mode(3, 1000, 1000, 120f)
         )
         displayModeDirector.injectSupportedModesByDisplay(SparseArray<Array<Display.Mode>>().apply {
             append(Display.DEFAULT_DISPLAY, modes)
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
index a8b792e..80f7a06 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
@@ -197,7 +197,7 @@
                     + Arrays.toString(invocation.getArguments()));
             if (!wedge) {
                 if (mRealAms.mConstants.mEnableWaitForFinishAttachApplication) {
-                    mRealAms.finishAttachApplication(0);
+                    mRealAms.finishAttachApplication(0, 0);
                 }
             }
             return null;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java
index 67be93b..89c67d5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java
@@ -200,7 +200,7 @@
             Log.v(TAG, "Intercepting bindApplication() for "
                     + Arrays.toString(invocation.getArguments()));
             if (mRealAms.mConstants.mEnableWaitForFinishAttachApplication) {
-                mRealAms.finishAttachApplication(0);
+                mRealAms.finishAttachApplication(0, 0);
             }
             return null;
         }).when(thread).bindApplication(
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
index e672928..599b9cd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
@@ -24,6 +24,7 @@
 import android.content.ContentResolver;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.provider.DeviceConfig.Properties;
 import android.text.TextUtils;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
@@ -42,6 +43,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Test SettingsToPropertiesMapper.
@@ -61,6 +63,7 @@
 
     private HashMap<String, String> mSystemSettingsMap;
     private HashMap<String, String> mGlobalSettingsMap;
+    private HashMap<String, String> mConfigSettingsMap;
 
     @Before
     public void setUp() throws Exception {
@@ -71,9 +74,11 @@
                         .spyStatic(SystemProperties.class)
                         .spyStatic(Settings.Global.class)
                         .spyStatic(SettingsToPropertiesMapper.class)
+                        .spyStatic(Settings.Config.class)
                         .startMocking();
         mSystemSettingsMap = new HashMap<>();
         mGlobalSettingsMap = new HashMap<>();
+        mConfigSettingsMap = new HashMap<>();
 
         // Mock SystemProperties setter and various getters
         doAnswer((Answer<Void>) invocationOnMock -> {
@@ -93,7 +98,7 @@
                 }
         ).when(() -> SystemProperties.get(anyString()));
 
-        // Mock Settings.Global methods
+        // Mock Settings.Global method
         doAnswer((Answer<String>) invocationOnMock -> {
                     String key = invocationOnMock.getArgument(1);
 
@@ -101,6 +106,21 @@
                 }
         ).when(() -> Settings.Global.getString(any(), anyString()));
 
+        // Mock Settings.Config getstrings method
+        doAnswer((Answer<Map<String, String>>) invocationOnMock -> {
+                    String namespace = invocationOnMock.getArgument(0);
+                    List<String> flags = invocationOnMock.getArgument(1);
+                    HashMap<String, String> values = new HashMap<>();
+                    for (String flag : flags) {
+                      String value = mConfigSettingsMap.get(namespace + "/" + flag);
+                      if (value != null) {
+                        values.put(flag, value);
+                      }
+                    }
+                    return values;
+                }
+        ).when(() -> Settings.Config.getStrings(anyString(), any()));
+
         mTestMapper = new SettingsToPropertiesMapper(
             mMockContentResolver, TEST_MAPPING, new String[] {}, new String[] {});
     }
@@ -239,4 +259,43 @@
         Assert.assertTrue(categories.contains("category2"));
         Assert.assertTrue(categories.contains("category3"));
     }
+
+  @Test
+  public void testGetStagedFlagsWithValueChange() {
+    // mock up what is in the setting already
+    mConfigSettingsMap.put("namespace_1/flag_1", "true");
+    mConfigSettingsMap.put("namespace_1/flag_2", "true");
+
+    // mock up input
+    String namespace = "staged";
+    Map<String, String> keyValueMap = new HashMap<>();
+    // case 1: existing prop, stage the same value
+    keyValueMap.put("namespace_1*flag_1", "true");
+    // case 2: existing prop, stage a different value
+    keyValueMap.put("namespace_1*flag_2", "false");
+    // case 3: new prop, stage the non default value
+    keyValueMap.put("namespace_2*flag_1", "true");
+    // case 4: new prop, stage the default value
+    keyValueMap.put("namespace_2*flag_2", "false");
+    Properties props = new Properties(namespace, keyValueMap);
+
+    HashMap<String, HashMap<String, String>> toStageProps =
+        SettingsToPropertiesMapper.getStagedFlagsWithValueChange(props);
+
+    HashMap<String, String> namespace_1_to_stage = toStageProps.get("namespace_1");
+    HashMap<String, String> namespace_2_to_stage = toStageProps.get("namespace_2");
+    Assert.assertTrue(namespace_1_to_stage != null);
+    Assert.assertTrue(namespace_2_to_stage != null);
+
+    String namespace_1_flag_1 = namespace_1_to_stage.get("flag_1");
+    String namespace_1_flag_2 = namespace_1_to_stage.get("flag_2");
+    String namespace_2_flag_1 = namespace_2_to_stage.get("flag_1");
+    String namespace_2_flag_2 = namespace_2_to_stage.get("flag_2");
+    Assert.assertTrue(namespace_1_flag_1 == null);
+    Assert.assertTrue(namespace_1_flag_2 != null);
+    Assert.assertTrue(namespace_2_flag_1 != null);
+    Assert.assertTrue(namespace_2_flag_2 == null);
+    Assert.assertTrue(namespace_1_flag_2.equals("false"));
+    Assert.assertTrue(namespace_2_flag_1.equals("true"));
+  }
 }
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index 5c74a80..7f165e0 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -3241,6 +3241,48 @@
         }
     }
 
+    @Test
+    public void testHalAutoSuspendMode_enabledByConfiguration() {
+        AtomicReference<DisplayManagerInternal.DisplayPowerCallbacks> callback =
+                new AtomicReference<>();
+        doAnswer((Answer<Void>) invocation -> {
+            callback.set(invocation.getArgument(0));
+            return null;
+        }).when(mDisplayManagerInternalMock).initPowerManagement(any(), any(), any());
+        when(mResourcesSpy.getBoolean(
+                com.android.internal.R.bool.config_powerDecoupleAutoSuspendModeFromDisplay))
+                .thenReturn(false);
+        when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_useAutoSuspend))
+                .thenReturn(true);
+
+        createService();
+        startSystem();
+        callback.get().onDisplayStateChange(/* allInactive= */ true, /* allOff= */ true);
+
+        verify(mNativeWrapperMock).nativeSetAutoSuspend(true);
+    }
+
+    @Test
+    public void testHalAutoSuspendMode_disabledByConfiguration() {
+        AtomicReference<DisplayManagerInternal.DisplayPowerCallbacks> callback =
+                new AtomicReference<>();
+        doAnswer((Answer<Void>) invocation -> {
+            callback.set(invocation.getArgument(0));
+            return null;
+        }).when(mDisplayManagerInternalMock).initPowerManagement(any(), any(), any());
+        when(mResourcesSpy.getBoolean(
+                com.android.internal.R.bool.config_powerDecoupleAutoSuspendModeFromDisplay))
+                .thenReturn(false);
+        when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_useAutoSuspend))
+                .thenReturn(false);
+
+        createService();
+        startSystem();
+        callback.get().onDisplayStateChange(/* allInactive= */ true, /* allOff= */ true);
+
+        verify(mNativeWrapperMock, never()).nativeSetAutoSuspend(true);
+    }
+
     private void setCachedUidProcState(int uid) {
         mService.updateUidProcStateInternal(uid, PROCESS_STATE_TOP_SLEEPING);
     }
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 94a71be..753db12 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -279,7 +279,7 @@
 test_module_config {
     name: "FrameworksServicesTests_contentprotection",
     base: "FrameworksServicesTests",
-    test_suites: ["general-tests"],
+    test_suites: ["device-tests"],
     include_filters: ["com.android.server.contentprotection"],
     exclude_annotations: FLAKY_AND_IGNORED,
 }
@@ -287,7 +287,7 @@
 test_module_config {
     name: "FrameworksServicesTests_om",
     base: "FrameworksServicesTests",
-    test_suites: ["general-tests"],
+    test_suites: ["device-tests"],
     include_filters: ["com.android.server.om."],
     exclude_annotations: FLAKY_AND_IGNORED,
 }
@@ -296,7 +296,7 @@
 test_module_config {
     name: "FrameworksServicesTests_contexthub_presubmit",
     base: "FrameworksServicesTests",
-    test_suites: ["general-tests"],
+    test_suites: ["device-tests"],
     include_filters: ["com.android.server.location.contexthub."],
     // TODO(ron): are these right, does it run anything?
     include_annotations: ["android.platform.test.annotations.Presubmit"],
@@ -306,7 +306,7 @@
 test_module_config {
     name: "FrameworksServicesTests_contexthub_postsubmit",
     base: "FrameworksServicesTests",
-    test_suites: ["general-tests"],
+    test_suites: ["device-tests"],
     include_filters: ["com.android.server.location.contexthub."],
     // TODO(ron): are these right, does it run anything?
     include_annotations: ["android.platform.test.annotations.Postsubmit"],
@@ -317,7 +317,7 @@
 test_module_config {
     name: "FrameworksServicesTests_contentcapture",
     base: "FrameworksServicesTests",
-    test_suites: ["general-tests"],
+    test_suites: ["device-tests"],
     include_filters: ["com.android.server.contentcapture"],
     exclude_annotations: FLAKY_AND_IGNORED,
 }
@@ -325,7 +325,7 @@
 test_module_config {
     name: "FrameworksServicesTests_recoverysystem",
     base: "FrameworksServicesTests",
-    test_suites: ["general-tests"],
+    test_suites: ["device-tests"],
     include_filters: ["com.android.server.recoverysystem."],
     exclude_annotations: ["androidx.test.filters.FlakyTest"],
 }
@@ -334,7 +334,7 @@
 test_module_config {
     name: "FrameworksServicesTests_pm_presubmit",
     base: "FrameworksServicesTests",
-    test_suites: ["general-tests"],
+    test_suites: ["device-tests"],
     include_annotations: ["android.platform.test.annotations.Presubmit"],
     include_filters: ["com.android.server.pm."],
     exclude_annotations: FLAKY_AND_IGNORED,
@@ -343,7 +343,7 @@
 test_module_config {
     name: "FrameworksServicesTests_pm_postsubmit",
     base: "FrameworksServicesTests",
-    test_suites: ["general-tests"],
+    test_suites: ["device-tests"],
     include_annotations: ["android.platform.test.annotations.Postsubmit"],
     include_filters: ["com.android.server.pm."],
     exclude_annotations: FLAKY_AND_IGNORED,
@@ -353,6 +353,6 @@
 test_module_config {
     name: "FrameworksServicesTests_os",
     base: "FrameworksServicesTests",
-    test_suites: ["general-tests"],
+    test_suites: ["device-tests"],
     include_filters: ["com.android.server.os."],
 }
diff --git a/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java b/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
index 669eedf..dabf531 100644
--- a/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
@@ -31,11 +31,13 @@
 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
 import android.os.Handler;
 import android.os.PowerManager;
+import android.os.RemoteException;
 import android.os.test.TestLooper;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableContext;
 import android.testing.TestableResources;
 import android.view.Window;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -48,7 +50,8 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 import java.util.List;
 
@@ -72,9 +75,15 @@
     private static final Integer AUTO_DISMISS_DIALOG = 500;
 
     @Rule
+    public MockitoRule rule = MockitoJUnit.rule();
+
+    @Rule
     public TestableContext mContext =
             new TestableContext(InstrumentationRegistry.getContext(), null);
 
+    private final AccessibilityManager mAccessibilityManager =
+            mContext.getSystemService(AccessibilityManager.class);
+
     @Mock
     private PackageManager mPackageManager;
     @Mock
@@ -89,9 +98,8 @@
     private BiometricStateListener mBiometricStateListener;
 
     @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-
+    public void setup() throws RemoteException {
+        disableAccessibility();
         mContext.addMockSystemService(PackageManager.class, mPackageManager);
         mContext.addMockSystemService(FingerprintManager.class, mFingerprintManager);
         TestableResources resources = mContext.getOrCreateTestableResources();
@@ -192,9 +200,8 @@
     }
 
     @Test
-    public void dialogDismissesAfterTime() throws Exception {
+    public void dialogDismissesAfterTime_accessibilityDisabled() throws Exception {
         setupWithSensor(true /* hasSfps */, true /* initialized */);
-
         setBiometricState(BiometricStateListener.STATE_ENROLLING);
         when(mDialog.isShowing()).thenReturn(true);
         assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
@@ -207,9 +214,23 @@
     }
 
     @Test
-    public void dialogDoesNotDismissOnSensorTouch() throws Exception {
+    public void dialogDoesNotDismissAfterTime_accessibilityEnabled() throws Exception {
+        enableAccessibility();
         setupWithSensor(true /* hasSfps */, true /* initialized */);
+        setBiometricState(BiometricStateListener.STATE_ENROLLING);
+        when(mDialog.isShowing()).thenReturn(true);
+        assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
 
+        mLooper.dispatchAll();
+        verify(mDialog).show();
+        mLooper.moveTimeForward(AUTO_DISMISS_DIALOG);
+        mLooper.dispatchAll();
+        verify(mDialog, never()).dismiss();
+    }
+
+    @Test
+    public void dialogDoesNotDismissOnSensorTouch_accessibilityDisabled() throws Exception {
+        setupWithSensor(true /* hasSfps */, true /* initialized */);
         setBiometricState(BiometricStateListener.STATE_ENROLLING);
         when(mDialog.isShowing()).thenReturn(true);
         assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
@@ -218,12 +239,26 @@
         verify(mDialog).show();
 
         mBiometricStateListener.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
-        mLooper.moveTimeForward(AUTO_DISMISS_DIALOG - 1);
         mLooper.dispatchAll();
-
         verify(mDialog, never()).dismiss();
     }
 
+    @Test
+    public void dialogDismissesOnSensorTouch_accessibilityEnabled() throws Exception {
+        enableAccessibility();
+        setupWithSensor(true /* hasSfps */, true /* initialized */);
+        setBiometricState(BiometricStateListener.STATE_ENROLLING);
+        when(mDialog.isShowing()).thenReturn(true);
+        assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
+
+        mLooper.dispatchAll();
+        verify(mDialog).show();
+
+        mBiometricStateListener.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
+        mLooper.dispatchAll();
+        verify(mDialog).dismiss();
+    }
+
     private void setBiometricState(@BiometricStateListener.State int newState) {
         if (mBiometricStateListener != null) {
             mBiometricStateListener.onStateChanged(newState);
@@ -231,6 +266,20 @@
         }
     }
 
+    private void enableAccessibility() throws RemoteException {
+        if (mAccessibilityManager != null) {
+            mAccessibilityManager.getClient().setState(1);
+            mLooper.dispatchAll();
+        }
+    }
+
+    private void disableAccessibility() throws RemoteException {
+        if (mAccessibilityManager != null) {
+            mAccessibilityManager.getClient().setState(0);
+            mLooper.dispatchAll();
+        }
+    }
+
     private void setupWithSensor(boolean hasSfps, boolean initialized) throws Exception {
         when(mPackageManager.hasSystemFeature(eq(PackageManager.FEATURE_FINGERPRINT)))
                 .thenReturn(true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 0d6fdc9..4af20a9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -2638,7 +2638,7 @@
 
     @Test
     public void testSoundResetsRankingTime() throws Exception {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_UPDATE_RANKING_TIME);
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_SORT_SECTION_BY_TIME);
         TestableFlagResolver flagResolver = new TestableFlagResolver();
         initAttentionHelper(flagResolver);
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 74d8433..3a0eba1 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -24,7 +24,7 @@
 import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.SHOW_IMMEDIATELY;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.Flags.FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS;
-import static android.app.Flags.FLAG_UPDATE_RANKING_TIME;
+import static android.app.Flags.FLAG_SORT_SECTION_BY_TIME;
 import static android.app.Notification.EXTRA_ALLOW_DURING_SETUP;
 import static android.app.Notification.EXTRA_PICTURE;
 import static android.app.Notification.EXTRA_PICTURE_ICON;
@@ -7376,7 +7376,7 @@
     }
 
     @Test
-    @EnableFlags({android.app.Flags.FLAG_UPDATE_RANKING_TIME})
+    @EnableFlags({android.app.Flags.FLAG_SORT_SECTION_BY_TIME})
     public void testVisualDifference_userInitiatedJob() {
         Notification.Builder nb1 = new Notification.Builder(mContext, "")
                 .setContentTitle("foo");
@@ -15283,7 +15283,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_UPDATE_RANKING_TIME)
+    @EnableFlags(FLAG_SORT_SECTION_BY_TIME)
     public void rankingTime_newNotification_noisy_matchesSbn() throws Exception {
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, mUserId);
 
@@ -15297,7 +15297,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_UPDATE_RANKING_TIME)
+    @EnableFlags(FLAG_SORT_SECTION_BY_TIME)
     public void rankingTime_newNotification_silent_matchesSbn() throws Exception {
         NotificationChannel low = new NotificationChannel("low", "low", IMPORTANCE_LOW);
         NotificationRecord nr = generateNotificationRecord(low, mUserId);
@@ -15312,7 +15312,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_UPDATE_RANKING_TIME)
+    @EnableFlags(FLAG_SORT_SECTION_BY_TIME)
     public void rankingTime_updatedNotification_silentSameText_originalPostTime() throws Exception {
         NotificationChannel low = new NotificationChannel("low", "low", IMPORTANCE_LOW);
         NotificationRecord nr = generateNotificationRecord(low, mUserId);
@@ -15332,7 +15332,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_UPDATE_RANKING_TIME)
+    @EnableFlags(FLAG_SORT_SECTION_BY_TIME)
     public void rankingTime_updatedNotification_silentNewText_newPostTime() throws Exception {
         NotificationChannel low = new NotificationChannel("low", "low", IMPORTANCE_LOW);
         NotificationRecord nr = generateNotificationRecord(low, 0, mUserId);
@@ -15357,7 +15357,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_UPDATE_RANKING_TIME)
+    @EnableFlags(FLAG_SORT_SECTION_BY_TIME)
     public void rankingTime_updatedNotification_noisySameText_newPostTime() throws Exception {
         NotificationChannel low = new NotificationChannel("low", "low", IMPORTANCE_LOW);
         NotificationRecord nr = generateNotificationRecord(low, mUserId);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 9a58594..d1880d2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -1561,14 +1561,14 @@
         mXmlHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
                 false, mClock);
-        loadByteArrayXml(baos.toByteArray(), false, USER_SYSTEM);
+        loadByteArrayXml(baos.toByteArray(), false, USER_ALL);
 
         // Trigger 2nd restore pass
-        when(mPm.getPackageUidAsUser(PKG_R, USER_SYSTEM)).thenReturn(UID_P);
+        when(mPm.getPackageUidAsUser(PKG_R, USER_SYSTEM)).thenReturn(UID_R);
         mXmlHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_R},
-                new int[]{UID_P});
+                new int[]{UID_R});
 
-        NotificationChannel channel = mXmlHelper.getNotificationChannel(PKG_R, UID_P, id,
+        NotificationChannel channel = mXmlHelper.getNotificationChannel(PKG_R, UID_R, id,
                 false);
         assertThat(channel.getImportance()).isEqualTo(2);
         assertThat(channel.canShowBadge()).isTrue();
@@ -1616,7 +1616,7 @@
         mXmlHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
                 false, mClock);
-        loadByteArrayXml(xml.getBytes(), false, USER_SYSTEM);
+        loadByteArrayXml(xml.getBytes(), false, USER_ALL);
 
         // Trigger 2nd restore pass
         mXmlHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_R},
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 527001d..9a6e818 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -378,7 +378,7 @@
     }
 
     @Test
-    @EnableFlags({android.app.Flags.FLAG_SORT_SECTION_BY_TIME, Flags.FLAG_UPDATE_RANKING_TIME})
+    @EnableFlags(android.app.Flags.FLAG_SORT_SECTION_BY_TIME)
     public void testSort_oldWhenChildren_unspecifiedSummary() {
         NotificationRecord child1 = new NotificationRecord(mContext,
                 new StatusBarNotification(
@@ -430,7 +430,7 @@
     }
 
     @Test
-    @EnableFlags({android.app.Flags.FLAG_SORT_SECTION_BY_TIME, Flags.FLAG_UPDATE_RANKING_TIME})
+    @EnableFlags(android.app.Flags.FLAG_SORT_SECTION_BY_TIME)
     public void testSort_oldChildren_unspecifiedSummary() {
         NotificationRecord child1 = new NotificationRecord(mContext,
                 new StatusBarNotification(
@@ -480,7 +480,7 @@
     }
 
     @Test
-    @EnableFlags({android.app.Flags.FLAG_SORT_SECTION_BY_TIME, Flags.FLAG_UPDATE_RANKING_TIME})
+    @EnableFlags(android.app.Flags.FLAG_SORT_SECTION_BY_TIME)
     public void testSort_oldChildren_oldSummary() {
         NotificationRecord child1 = new NotificationRecord(mContext,
                 new StatusBarNotification(
@@ -517,10 +517,10 @@
                         mUser, null, System.currentTimeMillis()), getLowChannel());
 
         ArrayList<NotificationRecord> expected = new ArrayList<>();
+        expected.add(unrelated);
         expected.add(summary);
         expected.add(child2);
         expected.add(child1);
-        expected.add(unrelated);
 
         ArrayList<NotificationRecord> actual = new ArrayList<>();
         actual.addAll(expected);
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 30eb5ef..1da5001 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -116,6 +116,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.when;
 
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
@@ -3507,6 +3508,23 @@
     }
 
     @Test
+    public void testIsCameraActive() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final DisplayRotationCompatPolicy displayRotationCompatPolicy = mock(
+                DisplayRotationCompatPolicy.class);
+        when(mDisplayContent.getDisplayRotationCompatPolicy()).thenReturn(
+                displayRotationCompatPolicy);
+
+        when(displayRotationCompatPolicy.isCameraActive(any(ActivityRecord.class),
+                anyBoolean())).thenReturn(false);
+        assertFalse(app.mActivityRecord.isCameraActive());
+
+        when(displayRotationCompatPolicy.isCameraActive(any(ActivityRecord.class),
+                anyBoolean())).thenReturn(true);
+        assertTrue(app.mActivityRecord.isCameraActive());
+    }
+
+    @Test
     public void testUpdateCameraCompatStateFromUser_clickedOnDismiss() throws RemoteException {
         final ActivityRecord activity = createActivityWithTask();
         // Mock a flag being enabled.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 7356b43..27d9d13 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1071,6 +1071,7 @@
     @Test
     public void testAllowsTopmostFullscreenOrientation() {
         final DisplayContent dc = createNewDisplay();
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, dc.getOrientation());
         dc.getDisplayRotation().setFixedToUserRotation(
                 IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
 
@@ -1717,7 +1718,6 @@
         // The display should be rotated after the launch is finished.
         doReturn(false).when(app).isAnimating(anyInt(), anyInt());
         mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token);
-        waitHandlerIdle(mWm.mH);
         mStatusBarWindow.finishSeamlessRotation(t);
         mNavBarWindow.finishSeamlessRotation(t);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index 507140d..262ba8b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -38,6 +38,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -538,6 +539,42 @@
         assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false);
     }
 
+    @Test
+    public void testIsCameraActiveWhenCallbackInvokedNoMultiWindow_returnTrue() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+        assertTrue(
+                mDisplayRotationCompatPolicy.isCameraActive(mActivity, /* mustBeFullscreen*/ true));
+    }
+
+    @Test
+    public void testIsCameraActiveWhenCallbackNotInvokedNoMultiWindow_returnFalse() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+        assertFalse(
+                mDisplayRotationCompatPolicy.isCameraActive(mActivity, /* mustBeFullscreen*/ true));
+    }
+
+    @Test
+    public void testIsCameraActiveWhenCallbackNotInvokedMultiWindow_returnFalse() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        when(mActivity.inMultiWindowMode()).thenReturn(true);
+
+        assertFalse(
+                mDisplayRotationCompatPolicy.isCameraActive(mActivity, /* mustBeFullscreen*/ true));
+    }
+
+    @Test
+    public void testIsCameraActiveWhenCallbackInvokedMultiWindow_returnFalse() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        when(mActivity.inMultiWindowMode()).thenReturn(true);
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+        assertFalse(
+                mDisplayRotationCompatPolicy.isCameraActive(mActivity, /* mustBeFullscreen*/ true));
+    }
+
     private void configureActivity(@ScreenOrientation int activityOrientation) {
         configureActivityAndDisplay(activityOrientation, ORIENTATION_PORTRAIT);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index b41db31..b74da1a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -29,6 +29,7 @@
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
 import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
 import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
 import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
@@ -73,6 +74,8 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
 import android.annotation.Nullable;
@@ -1086,6 +1089,39 @@
         assertFalse(mController.shouldApplyUserMinAspectRatioOverride());
     }
 
+    @Test
+    public void testRecomputeConfigurationForCameraCompatIfNeeded() {
+        spyOn(mController);
+        doReturn(false).when(mController).isOverrideOrientationOnlyForCameraEnabled();
+        doReturn(false).when(mController).isCameraCompatSplitScreenAspectRatioAllowed();
+        doReturn(false).when(mController).shouldOverrideMinAspectRatioForCamera();
+        clearInvocations(mActivity);
+
+        mController.recomputeConfigurationForCameraCompatIfNeeded();
+
+        verify(mActivity, never()).recomputeConfiguration();
+
+        // isOverrideOrientationOnlyForCameraEnabled
+        doReturn(true).when(mController).isOverrideOrientationOnlyForCameraEnabled();
+        clearInvocations(mActivity);
+        mController.recomputeConfigurationForCameraCompatIfNeeded();
+        verify(mActivity).recomputeConfiguration();
+
+        // isCameraCompatSplitScreenAspectRatioAllowed
+        doReturn(false).when(mController).isOverrideOrientationOnlyForCameraEnabled();
+        doReturn(true).when(mController).isCameraCompatSplitScreenAspectRatioAllowed();
+        clearInvocations(mActivity);
+        mController.recomputeConfigurationForCameraCompatIfNeeded();
+        verify(mActivity).recomputeConfiguration();
+
+        // shouldOverrideMinAspectRatioForCamera
+        doReturn(false).when(mController).isCameraCompatSplitScreenAspectRatioAllowed();
+        doReturn(true).when(mController).shouldOverrideMinAspectRatioForCamera();
+        clearInvocations(mActivity);
+        mController.recomputeConfigurationForCameraCompatIfNeeded();
+        verify(mActivity).recomputeConfiguration();
+    }
+
     private void prepareActivityForShouldApplyUserMinAspectRatioOverride(
             boolean orientationRequest) {
         spyOn(mController);
@@ -1280,6 +1316,78 @@
     }
 
     @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_overrideEnabled_returnsTrue() {
+        doReturn(true).when(mActivity).isCameraActive();
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertTrue(mController.shouldOverrideMinAspectRatioForCamera());
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsTrue()
+            throws Exception {
+        doReturn(true).when(mActivity).isCameraActive();
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertTrue(mController.shouldOverrideMinAspectRatioForCamera());
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsFalse()
+            throws Exception {
+        doReturn(false).when(mActivity).isCameraActive();
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldOverrideMinAspectRatioForCamera());
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideDisabled_returnsFalse()
+            throws Exception {
+        doReturn(true).when(mActivity).isCameraActive();
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldOverrideMinAspectRatioForCamera());
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_overrideDisabled_returnsFalse() {
+        doReturn(true).when(mActivity).isCameraActive();
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldOverrideMinAspectRatioForCamera());
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyFalse_overrideEnabled_returnsFalse()
+            throws Exception {
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldOverrideMinAspectRatioForCamera());
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyFalse_noOverride_returnsFalse()
+            throws Exception {
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
+        doReturn(true).when(mActivity).isCameraActive();
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldOverrideMinAspectRatioForCamera());
+    }
+
+    @Test
     @EnableCompatChanges({FORCE_RESIZE_APP})
     public void testshouldOverrideForceResizeApp_overrideEnabled_returnsTrue() {
         mController = new LetterboxUiController(mWm, mActivity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 48fc2dc..9f85acb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -565,36 +565,17 @@
 
     @Test
     public void testGetOrientation_childSpecified() {
-        testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_LANDSCAPE,
-                SCREEN_ORIENTATION_LANDSCAPE);
-        testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_UNSET,
-                SCREEN_ORIENTATION_UNSPECIFIED);
-    }
-
-    private void testGetOrientation_childSpecifiedConfig(boolean childVisible, int childOrientation,
-            int expectedOrientation) {
         final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
-        final TestWindowContainer root = builder.setLayer(0).build();
+        final TestWindowContainer root = builder.build();
         root.setFillsParent(true);
+        assertEquals(SCREEN_ORIENTATION_UNSET, root.getOrientation());
 
-        builder.setIsVisible(childVisible);
+        final TestWindowContainer child = root.addChildWindow();
+        child.setFillsParent(true);
+        assertEquals(SCREEN_ORIENTATION_UNSET, root.getOrientation());
 
-        if (childOrientation != SCREEN_ORIENTATION_UNSET) {
-            builder.setOrientation(childOrientation);
-        }
-
-        final TestWindowContainer child1 = root.addChildWindow(builder);
-        child1.setFillsParent(true);
-
-        assertEquals(expectedOrientation, root.getOrientation());
-    }
-
-    @Test
-    public void testGetOrientation_Unset() {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
-        final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
-        // Unspecified well because we didn't specify anything...
-        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, root.getOrientation());
+        child.setOverrideOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, root.getOrientation());
     }
 
     @Test
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index a35a35a..9d14290 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1241,7 +1241,7 @@
                     break;
                 case Event.SHORTCUT_INVOCATION:
                 case Event.CHOOSER_ACTION:
-                case Event.STANDBY_BUCKET_CHANGED:
+                // case Event.STANDBY_BUCKET_CHANGED:
                 case Event.FOREGROUND_SERVICE_START:
                 case Event.FOREGROUND_SERVICE_STOP:
                     logAppUsageEventReportedAtomLocked(event.mEventType, uid, event.mPackage);
diff --git a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
index 439cf13..1dc1037 100644
--- a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
@@ -8,6 +8,8 @@
     <option name="isolated-storage" value="false"/>
 
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
         <!-- prevents the phone from restarting -->
diff --git a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
index 4b6224e..57a58c8 100644
--- a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
@@ -8,6 +8,8 @@
     <option name="isolated-storage" value="false"/>
 
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
         <!-- prevents the phone from restarting -->
diff --git a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
index 583bcb7..2cb86e0 100644
--- a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
@@ -8,6 +8,8 @@
     <option name="isolated-storage" value="false"/>
 
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
         <!-- prevents the phone from restarting -->
diff --git a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
index d6ae2b3..2cf85fa 100644
--- a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
@@ -8,6 +8,8 @@
     <option name="isolated-storage" value="false"/>
 
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
         <!-- prevents the phone from restarting -->
diff --git a/tests/FlickerTests/IME/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml
index 38442db..b93e1be 100644
--- a/tests/FlickerTests/IME/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml
@@ -8,6 +8,8 @@
     <option name="isolated-storage" value="false"/>
 
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
         <!-- enable AOD -->
diff --git a/tests/FlickerTests/Notification/AndroidTestTemplate.xml b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
index 4036858..9c6a17d3 100644
--- a/tests/FlickerTests/Notification/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
@@ -8,6 +8,8 @@
     <option name="isolated-storage" value="false"/>
 
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
         <!-- prevents the phone from restarting -->
diff --git a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
index 797ca4e..ecbed28 100644
--- a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
@@ -8,6 +8,8 @@
     <option name="isolated-storage" value="false"/>
 
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
         <!-- prevents the phone from restarting -->
diff --git a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
index b5ea739..1eacdfd 100644
--- a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
@@ -8,6 +8,8 @@
     <option name="isolated-storage" value="false"/>
 
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
         <!-- prevents the phone from restarting -->