Merge "Use String for display name in VirtualCameraConfig" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index efd8578..c231b30 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -57,7 +57,7 @@
     ":android.app.flags-aconfig-java{.generated_srcjars}",
     ":android.credentials.flags-aconfig-java{.generated_srcjars}",
     ":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}",
-    ":com.android.server.flags.pinner-aconfig-java{.generated_srcjars}",
+    ":com.android.server.flags.services-aconfig-java{.generated_srcjars}",
     ":android.service.controls.flags-aconfig-java{.generated_srcjars}",
     ":android.service.voice.flags-aconfig-java{.generated_srcjars}",
     ":android.media.tv.flags-aconfig-java{.generated_srcjars}",
@@ -588,16 +588,16 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
-// Pinner Service
+// Server Services Flags
 aconfig_declarations {
-    name: "com.android.server.flags.pinner-aconfig",
+    name: "com.android.server.flags.services-aconfig",
     package: "com.android.server.flags",
-    srcs: ["services/core/java/com/android/server/flags/pinner.aconfig"],
+    srcs: ["services/core/java/com/android/server/flags/*.aconfig"],
 }
 
 java_aconfig_library {
-    name: "com.android.server.flags.pinner-aconfig-java",
-    aconfig_declarations: "com.android.server.flags.pinner-aconfig",
+    name: "com.android.server.flags.services-aconfig-java",
+    aconfig_declarations: "com.android.server.flags.services-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
diff --git a/SECURITY_STATE_OWNERS b/SECURITY_STATE_OWNERS
new file mode 100644
index 0000000..30ddfe2
--- /dev/null
+++ b/SECURITY_STATE_OWNERS
@@ -0,0 +1,5 @@
+alxu@google.com
+musashi@google.com
+maunik@google.com
+davidkwak@google.com
+willcoster@google.com
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 900c902..d940e38 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1830,7 +1830,12 @@
                     /* system_measured_calling_download_bytes */0,
                     /* system_measured_calling_upload_bytes */ 0,
                     jobStatus.getJob().getIntervalMillis(),
-                    jobStatus.getJob().getFlexMillis());
+                    jobStatus.getJob().getFlexMillis(),
+                    jobStatus.hasFlexibilityConstraint(),
+                    /* isFlexConstraintSatisfied */ false,
+                    jobStatus.canApplyTransportAffinities(),
+                    jobStatus.getNumAppliedFlexibleConstraints(),
+                    jobStatus.getNumDroppedFlexibleConstraints());
 
             // If the job is immediately ready to run, then we can just immediately
             // put it in the pending list and try to schedule it.  This is especially
@@ -2273,7 +2278,12 @@
                     /* system_measured_calling_download_bytes */0,
                     /* system_measured_calling_upload_bytes */ 0,
                     cancelled.getJob().getIntervalMillis(),
-                    cancelled.getJob().getFlexMillis());
+                    cancelled.getJob().getFlexMillis(),
+                    cancelled.hasFlexibilityConstraint(),
+                    cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_FLEXIBLE),
+                    cancelled.canApplyTransportAffinities(),
+                    cancelled.getNumAppliedFlexibleConstraints(),
+                    cancelled.getNumDroppedFlexibleConstraints());
         }
         // If this is a replacement, bring in the new version of the job
         if (incomingJob != null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 3addf9f..fe55e27 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -536,7 +536,12 @@
                     /* system_measured_calling_download_bytes */ 0,
                     /* system_measured_calling_upload_bytes */ 0,
                     job.getJob().getIntervalMillis(),
-                    job.getJob().getFlexMillis());
+                    job.getJob().getFlexMillis(),
+                    job.hasFlexibilityConstraint(),
+                    job.isConstraintSatisfied(JobStatus.CONSTRAINT_FLEXIBLE),
+                    job.canApplyTransportAffinities(),
+                    job.getNumAppliedFlexibleConstraints(),
+                    job.getNumDroppedFlexibleConstraints());
             sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
             final String sourcePackage = job.getSourcePackageName();
             if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
@@ -1620,7 +1625,12 @@
                 TrafficStats.getUidTxBytes(completedJob.getUid())
                         - mInitialUploadedBytesFromCalling,
                 completedJob.getJob().getIntervalMillis(),
-                completedJob.getJob().getFlexMillis());
+                completedJob.getJob().getFlexMillis(),
+                completedJob.hasFlexibilityConstraint(),
+                completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_FLEXIBLE),
+                completedJob.canApplyTransportAffinities(),
+                completedJob.getNumAppliedFlexibleConstraints(),
+                completedJob.getNumDroppedFlexibleConstraints());
         if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
             Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
                     getId());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 0d5d11e..bdc2246 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -106,11 +106,8 @@
     public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
     public static final long NO_EARLIEST_RUNTIME = 0L;
 
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE;  // 1 << 2
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public static final int CONSTRAINT_BATTERY_NOT_LOW =
             JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@@ -125,7 +122,7 @@
     static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24;      // Implicit constraint
     static final int CONSTRAINT_PREFETCH = 1 << 23;
     static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
-    static final int CONSTRAINT_FLEXIBLE = 1 << 21; // Implicit constraint
+    public static final int CONSTRAINT_FLEXIBLE = 1 << 21; // Implicit constraint
 
     private static final int IMPLICIT_CONSTRAINTS = 0
             | CONSTRAINT_BACKGROUND_NOT_RESTRICTED
@@ -1534,7 +1531,7 @@
 
     /**
      * Returns the number of required flexible job constraints that have been dropped with time.
-     * The lower this number is the easier it is for the flexibility constraint to be satisfied.
+     * The higher this number is the easier it is for the flexibility constraint to be satisfied.
      */
     public int getNumDroppedFlexibleConstraints() {
         return mNumDroppedFlexibleConstraints;
@@ -1600,7 +1597,8 @@
         mTransportAffinitiesSatisfied = isSatisfied;
     }
 
-    boolean canApplyTransportAffinities() {
+    /** Whether transport affinities can be applied to the job in flex scheduling. */
+    public boolean canApplyTransportAffinities() {
         return mCanApplyTransportAffinities;
     }
 
diff --git a/core/api/current.txt b/core/api/current.txt
index ace00fc..43ff0c9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9315,10 +9315,14 @@
   public final class StorageStats implements android.os.Parcelable {
     method public int describeContents();
     method public long getAppBytes();
+    method @FlaggedApi("android.app.usage.get_app_bytes_by_data_type_api") public long getAppBytesByDataType(int);
     method public long getCacheBytes();
     method public long getDataBytes();
     method public long getExternalCacheBytes();
     method public void writeToParcel(android.os.Parcel, int);
+    field @FlaggedApi("android.app.usage.get_app_bytes_by_data_type_api") public static final int APP_DATA_TYPE_FILE_TYPE_APK = 0; // 0x0
+    field @FlaggedApi("android.app.usage.get_app_bytes_by_data_type_api") public static final int APP_DATA_TYPE_FILE_TYPE_DM = 1; // 0x1
+    field @FlaggedApi("android.app.usage.get_app_bytes_by_data_type_api") public static final int APP_DATA_TYPE_LIB = 2; // 0x2
     field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.StorageStats> CREATOR;
   }
 
@@ -13418,12 +13422,12 @@
 
   public final class SigningInfo implements android.os.Parcelable {
     ctor public SigningInfo();
-    ctor @FlaggedApi("android.content.pm.archiving") public SigningInfo(@IntRange(from=0) int, @Nullable java.util.Collection<android.content.pm.Signature>, @Nullable java.util.Collection<java.security.PublicKey>, @Nullable java.util.Collection<android.content.pm.Signature>);
+    ctor @FlaggedApi("android.content.pm.archiving") public SigningInfo(int, @Nullable java.util.Collection<android.content.pm.Signature>, @Nullable java.util.Collection<java.security.PublicKey>, @Nullable java.util.Collection<android.content.pm.Signature>);
     ctor public SigningInfo(android.content.pm.SigningInfo);
     method public int describeContents();
     method public android.content.pm.Signature[] getApkContentsSigners();
     method @FlaggedApi("android.content.pm.archiving") @NonNull public java.util.Collection<java.security.PublicKey> getPublicKeys();
-    method @FlaggedApi("android.content.pm.archiving") @IntRange(from=0) public int getSchemeVersion();
+    method @FlaggedApi("android.content.pm.archiving") public int getSchemeVersion();
     method public android.content.pm.Signature[] getSigningCertificateHistory();
     method public boolean hasMultipleSigners();
     method public boolean hasPastSigningCertificates();
@@ -23972,6 +23976,7 @@
     method @Nullable public android.net.Uri getIconUri();
     method @NonNull public String getId();
     method @NonNull public CharSequence getName();
+    method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getSuitabilityStatus();
     method public int getType();
     method public int getVolume();
     method public int getVolumeHandling();
@@ -23989,6 +23994,9 @@
     field public static final String FEATURE_REMOTE_VIDEO_PLAYBACK = "android.media.route.feature.REMOTE_VIDEO_PLAYBACK";
     field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
     field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1
+    field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER = 2; // 0x2
+    field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER = 0; // 0x0
+    field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER = 1; // 0x1
     field public static final int TYPE_BLE_HEADSET = 26; // 0x1a
     field public static final int TYPE_BLUETOOTH_A2DP = 8; // 0x8
     field public static final int TYPE_BUILTIN_SPEAKER = 2; // 0x2
@@ -24029,6 +24037,7 @@
     method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence);
     method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle);
     method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri);
+    method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.MediaRoute2Info.Builder setSuitabilityStatus(int);
     method @NonNull public android.media.MediaRoute2Info.Builder setType(int);
     method @NonNull public android.media.MediaRoute2Info.Builder setVisibilityPublic();
     method @NonNull public android.media.MediaRoute2Info.Builder setVisibilityRestricted(@NonNull java.util.Set<java.lang.String>);
@@ -24242,6 +24251,7 @@
     method public void release();
     method public void selectRoute(@NonNull android.media.MediaRoute2Info);
     method public void setVolume(int);
+    method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public boolean wasTransferRequestedBySelf();
   }
 
   public abstract static class MediaRouter2.TransferCallback {
@@ -24639,12 +24649,16 @@
     method @Nullable public CharSequence getName();
     method @NonNull public java.util.List<java.lang.String> getSelectableRoutes();
     method @NonNull public java.util.List<java.lang.String> getSelectedRoutes();
+    method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getTransferReason();
     method @NonNull public java.util.List<java.lang.String> getTransferableRoutes();
     method public int getVolume();
     method public int getVolumeHandling();
     method public int getVolumeMax();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.media.RoutingSessionInfo> CREATOR;
+    field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int TRANSFER_REASON_APP = 2; // 0x2
+    field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int TRANSFER_REASON_FALLBACK = 0; // 0x0
+    field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int TRANSFER_REASON_SYSTEM_REQUEST = 1; // 0x1
   }
 
   public static final class RoutingSessionInfo.Builder {
@@ -24665,6 +24679,8 @@
     method @NonNull public android.media.RoutingSessionInfo.Builder removeTransferableRoute(@NonNull String);
     method @NonNull public android.media.RoutingSessionInfo.Builder setControlHints(@Nullable android.os.Bundle);
     method @NonNull public android.media.RoutingSessionInfo.Builder setName(@Nullable CharSequence);
+    method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.RoutingSessionInfo.Builder setTransferInitiator(@Nullable android.os.UserHandle, @Nullable String);
+    method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.RoutingSessionInfo.Builder setTransferReason(int);
     method @NonNull public android.media.RoutingSessionInfo.Builder setVolume(int);
     method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeHandling(int);
     method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeMax(int);
@@ -57395,7 +57411,7 @@
     method public abstract boolean getBuiltInZoomControls();
     method public abstract int getCacheMode();
     method public abstract String getCursiveFontFamily();
-    method public abstract boolean getDatabaseEnabled();
+    method @Deprecated public abstract boolean getDatabaseEnabled();
     method @Deprecated public abstract String getDatabasePath();
     method public abstract int getDefaultFixedFontSize();
     method public abstract int getDefaultFontSize();
@@ -57441,7 +57457,7 @@
     method public abstract void setBuiltInZoomControls(boolean);
     method public abstract void setCacheMode(int);
     method public abstract void setCursiveFontFamily(String);
-    method public abstract void setDatabaseEnabled(boolean);
+    method @Deprecated public abstract void setDatabaseEnabled(boolean);
     method @Deprecated public abstract void setDatabasePath(String);
     method public abstract void setDefaultFixedFontSize(int);
     method public abstract void setDefaultFontSize(int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index aaeba66..812ba6d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -510,6 +510,7 @@
     method public int getActivityType();
     method @Nullable public android.graphics.Rect getAppBounds();
     method @NonNull public android.graphics.Rect getBounds();
+    method public int getDisplayRotation();
     method @NonNull public android.graphics.Rect getMaxBounds();
     method public int getRotation();
     method public int getWindowingMode();
@@ -3611,6 +3612,10 @@
     method @Nullable public android.view.View getStatusBarBackgroundView();
   }
 
+  public static final class WindowInsets.Type {
+    method @NonNull public static String toString(int);
+  }
+
   public interface WindowManager extends android.view.ViewManager {
     method public default int getDisplayImePolicy(int);
     method public static boolean hasWindowExtensionsEnabled();
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 4621634..aa3b71a 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -27,6 +27,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Configuration;
@@ -326,7 +327,7 @@
     }
 
     /**
-     * Sets the apparent display cutout.
+     * Sets the display rotation.
      * @hide
      */
     public void setDisplayRotation(@Surface.Rotation int rotation) {
@@ -386,9 +387,9 @@
     }
 
     /**
-     * @see #setDisplayRotation
-     * @hide
+     * Gets the display rotation.
      */
+    @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
     public @Surface.Rotation int getDisplayRotation() {
         return mDisplayRotation;
     }
diff --git a/core/java/android/app/usage/StorageStats.java b/core/java/android/app/usage/StorageStats.java
index 8d25d7b..87d97d5 100644
--- a/core/java/android/app/usage/StorageStats.java
+++ b/core/java/android/app/usage/StorageStats.java
@@ -17,11 +17,16 @@
 package android.app.usage;
 
 import android.annotation.BytesLong;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
 import android.content.Context;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Storage statistics for a UID, package, or {@link UserHandle} on a single
  * storage volume.
@@ -29,10 +34,47 @@
  * @see StorageStatsManager
  */
 public final class StorageStats implements Parcelable {
-    /** {@hide} */ public long codeBytes;
-    /** {@hide} */ public long dataBytes;
-    /** {@hide} */ public long cacheBytes;
-    /** {@hide} */ public long externalCacheBytes;
+    /** @hide */ public long codeBytes;
+    /** @hide */ public long dataBytes;
+    /** @hide */ public long cacheBytes;
+    /** @hide */ public long apkBytes;
+    /** @hide */ public long libBytes;
+    /** @hide */ public long dmBytes;
+    /** @hide */ public long externalCacheBytes;
+
+    /** Represents all .apk files in application code path.
+     * Can be used as an input to {@link #getAppBytesByDataType(int)}
+     * to get the sum of sizes for files of this type.
+     */
+    @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+    public static final int APP_DATA_TYPE_FILE_TYPE_APK = 0;
+
+    /** Represents all .dm files in application code path.
+     * Can be used as an input to {@link #getAppBytesByDataType(int)}
+     * to get the sum of sizes for files of this type.
+     */
+    @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+    public static final int APP_DATA_TYPE_FILE_TYPE_DM = 1;
+
+    /** Represents lib/ in application code path.
+     * Can be used as an input to {@link #getAppBytesByDataType(int)}
+     * to get the size of lib/ directory.
+     */
+    @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+    public static final int APP_DATA_TYPE_LIB = 2;
+
+    /**
+     * Keep in sync with the file types defined above.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+    @IntDef(flag = false, value = {
+        APP_DATA_TYPE_FILE_TYPE_APK,
+        APP_DATA_TYPE_FILE_TYPE_DM,
+        APP_DATA_TYPE_LIB,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AppDataType {}
 
     /**
      * Return the size of app. This includes {@code APK} files, optimized
@@ -48,6 +90,27 @@
     }
 
     /**
+     * Return the size of the specified data type. This includes files stored under
+     * application code path.
+     * <p>
+     * If there is more than one package inside a uid, the return represents the aggregated
+     * stats when query StorageStat for package or uid.
+     * The data  is not collected and the return defaults to 0 when query StorageStats for user.
+     *
+     * <p>
+     * Data is isolated for each user on a multiuser device.
+     */
+    @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+    public long getAppBytesByDataType(@AppDataType int dataType) {
+        switch (dataType) {
+          case APP_DATA_TYPE_FILE_TYPE_APK: return apkBytes;
+          case APP_DATA_TYPE_LIB: return libBytes;
+          case APP_DATA_TYPE_FILE_TYPE_DM: return dmBytes;
+          default: return 0;
+        }
+    }
+
+    /**
      * Return the size of all data. This includes files stored under
      * {@link Context#getDataDir()}, {@link Context#getCacheDir()},
      * {@link Context#getCodeCacheDir()}.
@@ -98,6 +161,9 @@
         this.codeBytes = in.readLong();
         this.dataBytes = in.readLong();
         this.cacheBytes = in.readLong();
+        this.apkBytes = in.readLong();
+        this.libBytes = in.readLong();
+        this.dmBytes = in.readLong();
         this.externalCacheBytes = in.readLong();
     }
 
@@ -111,6 +177,9 @@
         dest.writeLong(codeBytes);
         dest.writeLong(dataBytes);
         dest.writeLong(cacheBytes);
+        dest.writeLong(apkBytes);
+        dest.writeLong(libBytes);
+        dest.writeLong(dmBytes);
         dest.writeLong(externalCacheBytes);
     }
 
diff --git a/core/java/android/app/usage/flags.aconfig b/core/java/android/app/usage/flags.aconfig
index a611255..4d9d911 100644
--- a/core/java/android/app/usage/flags.aconfig
+++ b/core/java/android/app/usage/flags.aconfig
@@ -35,3 +35,10 @@
     description: " Feature flag to support filter based event query API"
     bug: "194321117"
 }
+
+flag {
+    name: "get_app_bytes_by_data_type_api"
+    namespace: "system_performance"
+    description: "Feature flag for collecting app data size by file type API"
+    bug: "294088945"
+}
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index c95b864..ec2e5fe 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -12,4 +12,11 @@
   namespace: "app_widgets"
   description: "Enable adapter conversion to RemoteCollectionItemsAdapter"
   bug: "245950570"
-}
\ No newline at end of file
+}
+
+flag {
+  name: "remove_app_widget_service_io_from_critical_path"
+  namespace: "app_widgets"
+  description: "Move state file IO to non-critical path"
+  bug: "312949280"
+}
diff --git a/core/java/android/content/pm/PackageStats.java b/core/java/android/content/pm/PackageStats.java
index 81cfc07..b919c4b 100644
--- a/core/java/android/content/pm/PackageStats.java
+++ b/core/java/android/content/pm/PackageStats.java
@@ -55,6 +55,18 @@
     /** Size of cache used by the application. (e.g., /data/data/<app>/cache) */
     public long cacheSize;
 
+    /** Size of .apk files of the application. */
+    /** @hide */
+    public long apkSize;
+
+    /** Size of the libraries of the application. */
+    /** @hide */
+    public long libSize;
+
+    /** Size of the .dm files of the application. */
+    /** @hide */
+    public long dmSize;
+
     /**
      * Size of the secure container on external storage holding the
      * application's code.
@@ -108,6 +120,18 @@
             sb.append(" cache=");
             sb.append(cacheSize);
         }
+        if (apkSize != 0) {
+            sb.append(" apk=");
+            sb.append(apkSize);
+        }
+        if (libSize != 0) {
+            sb.append(" lib=");
+            sb.append(libSize);
+        }
+        if (dmSize != 0) {
+            sb.append(" dm=");
+            sb.append(dmSize);
+        }
         if (externalCodeSize != 0) {
             sb.append(" extCode=");
             sb.append(externalCodeSize);
@@ -149,6 +173,9 @@
         codeSize = source.readLong();
         dataSize = source.readLong();
         cacheSize = source.readLong();
+        apkSize = source.readLong();
+        libSize = source.readLong();
+        dmSize = source.readLong();
         externalCodeSize = source.readLong();
         externalDataSize = source.readLong();
         externalCacheSize = source.readLong();
@@ -162,6 +189,9 @@
         codeSize = pStats.codeSize;
         dataSize = pStats.dataSize;
         cacheSize = pStats.cacheSize;
+        apkSize = pStats.apkSize;
+        libSize = pStats.libSize;
+        dmSize = pStats.dmSize;
         externalCodeSize = pStats.externalCodeSize;
         externalDataSize = pStats.externalDataSize;
         externalCacheSize = pStats.externalCacheSize;
@@ -179,6 +209,9 @@
         dest.writeLong(codeSize);
         dest.writeLong(dataSize);
         dest.writeLong(cacheSize);
+        dest.writeLong(apkSize);
+        dest.writeLong(libSize);
+        dest.writeLong(dmSize);
         dest.writeLong(externalCodeSize);
         dest.writeLong(externalDataSize);
         dest.writeLong(externalCacheSize);
@@ -198,6 +231,9 @@
                 && codeSize == otherStats.codeSize
                 && dataSize == otherStats.dataSize
                 && cacheSize == otherStats.cacheSize
+                && apkSize == otherStats.apkSize
+                && libSize == otherStats.libSize
+                && dmSize == otherStats.dmSize
                 && externalCodeSize == otherStats.externalCodeSize
                 && externalDataSize == otherStats.externalDataSize
                 && externalCacheSize == otherStats.externalCacheSize
@@ -208,7 +244,8 @@
     @Override
     public int hashCode() {
         return Objects.hash(packageName, userHandle, codeSize, dataSize,
-                cacheSize, externalCodeSize, externalDataSize, externalCacheSize, externalMediaSize,
+                apkSize, libSize, dmSize, cacheSize, externalCodeSize,
+                externalDataSize, externalCacheSize, externalMediaSize,
                 externalObbSize);
     }
 
diff --git a/core/java/android/content/pm/SigningDetails.java b/core/java/android/content/pm/SigningDetails.java
index 8c21974..bb09ad2 100644
--- a/core/java/android/content/pm/SigningDetails.java
+++ b/core/java/android/content/pm/SigningDetails.java
@@ -31,6 +31,8 @@
 
 import libcore.util.HexEncoding;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.security.PublicKey;
 import java.security.cert.CertificateException;
 import java.util.ArrayList;
@@ -49,6 +51,7 @@
 
     private static final String TAG = "SigningDetails";
 
+    @Retention(RetentionPolicy.SOURCE)
     @IntDef({SignatureSchemeVersion.UNKNOWN,
             SignatureSchemeVersion.JAR,
             SignatureSchemeVersion.SIGNING_BLOCK_V2,
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
index a407704..23daaf2 100644
--- a/core/java/android/content/pm/SigningInfo.java
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -17,9 +17,9 @@
 package android.content.pm;
 
 import android.annotation.FlaggedApi;
-import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArraySet;
@@ -53,7 +53,7 @@
      * schemas</a>
      */
     @FlaggedApi(Flags.FLAG_ARCHIVING)
-    public SigningInfo(@IntRange(from = 0) int schemeVersion,
+    public SigningInfo(@SignatureSchemeVersion int schemeVersion,
             @Nullable Collection<Signature> apkContentsSigners,
             @Nullable Collection<PublicKey> publicKeys,
             @Nullable Collection<Signature> signingCertificateHistory) {
@@ -168,7 +168,7 @@
      * schemas</a>
      */
     @FlaggedApi(Flags.FLAG_ARCHIVING)
-    public @IntRange(from = 0) int getSchemeVersion() {
+    public @SignatureSchemeVersion int getSchemeVersion() {
         return mSigningDetails.getSignatureSchemeVersion();
     }
 
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 60d5c14..94bec35 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -123,3 +123,11 @@
     bug: "306329516"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "improve_home_app_behavior"
+    namespace: "package_manager_service"
+    description: "Feature flag to improve the uninstallation and preferred activity of home app."
+    bug: "310801107"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 209a595..d3f2c7a 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -92,3 +92,6 @@
 per-file IThermal* = file:/THERMAL_OWNERS
 per-file CoolingDevice.java = file:/THERMAL_OWNERS
 per-file Temperature.java = file:/THERMAL_OWNERS
+
+# SecurityStateManager
+per-file *SecurityStateManager* = file:/SECURITY_STATE_OWNERS
\ No newline at end of file
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4af657d..942ce971 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14339,6 +14339,14 @@
         public static final String MODE_RINGER = "mode_ringer";
 
         /**
+         * Whether or not Alarm stream should always be muted with Ringer.
+         *
+         * @hide
+         */
+        public static final String MUTE_ALARM_STREAM_WITH_RINGER_MODE =
+                "mute_alarm_stream_with_ringer_mode";
+
+        /**
          * Overlay display devices setting.
          * The associated value is a specially formatted string that describes the
          * size and density of simulated secondary display devices.
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index cabab6c..0927d45 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -17,7 +17,6 @@
 package android.view;
 
 import static android.view.InsetsSourceProto.FRAME;
-import static android.view.InsetsSourceProto.TYPE;
 import static android.view.InsetsSourceProto.TYPE_NUMBER;
 import static android.view.InsetsSourceProto.VISIBLE;
 import static android.view.InsetsSourceProto.VISIBLE_FRAME;
@@ -353,13 +352,12 @@
      */
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
-        proto.write(TYPE, WindowInsets.Type.toString(mType));
-        proto.write(TYPE_NUMBER, mType);
         mFrame.dumpDebug(proto, FRAME);
         if (mVisibleFrame != null) {
             mVisibleFrame.dumpDebug(proto, VISIBLE_FRAME);
         }
         proto.write(VISIBLE, mVisible);
+        proto.write(TYPE_NUMBER, mType);
         proto.end(token);
     }
 
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 34b2884..0ce61bb 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -21,11 +21,11 @@
 import static android.view.InsetsController.DEBUG;
 import static android.view.InsetsSourceConsumerProto.ANIMATION_STATE;
 import static android.view.InsetsSourceConsumerProto.HAS_WINDOW_FOCUS;
-import static android.view.InsetsSourceConsumerProto.INTERNAL_INSETS_TYPE;
 import static android.view.InsetsSourceConsumerProto.IS_REQUESTED_VISIBLE;
 import static android.view.InsetsSourceConsumerProto.PENDING_FRAME;
 import static android.view.InsetsSourceConsumerProto.PENDING_VISIBLE_FRAME;
 import static android.view.InsetsSourceConsumerProto.SOURCE_CONTROL;
+import static android.view.InsetsSourceConsumerProto.TYPE_NUMBER;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 
@@ -393,7 +393,6 @@
 
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
-        proto.write(INTERNAL_INSETS_TYPE, WindowInsets.Type.toString(mType));
         proto.write(HAS_WINDOW_FOCUS, mHasWindowFocus);
         proto.write(IS_REQUESTED_VISIBLE, isShowRequested());
         if (mSourceControl != null) {
@@ -406,6 +405,7 @@
             mPendingVisibleFrame.dumpDebug(proto, PENDING_VISIBLE_FRAME);
         }
         proto.write(ANIMATION_STATE, mAnimationState);
+        proto.write(TYPE_NUMBER, mType);
         proto.end(token);
     }
 }
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 7ea93f5..527c7ed 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -20,7 +20,7 @@
 import static android.graphics.PointProto.Y;
 import static android.view.InsetsSourceControlProto.LEASH;
 import static android.view.InsetsSourceControlProto.POSITION;
-import static android.view.InsetsSourceControlProto.TYPE;
+import static android.view.InsetsSourceControlProto.TYPE_NUMBER;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -244,8 +244,6 @@
      */
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
-        proto.write(TYPE, WindowInsets.Type.toString(mType));
-
         final long surfaceToken = proto.start(POSITION);
         proto.write(X, mSurfacePosition.x);
         proto.write(Y, mSurfacePosition.y);
@@ -254,6 +252,8 @@
         if (mLeash != null) {
             mLeash.dumpDebug(proto, LEASH);
         }
+
+        proto.write(TYPE_NUMBER, mType);
         proto.end(token);
     }
 
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index cbbe785..b957b31 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -22,7 +22,6 @@
 import static android.graphics.Matrix.MSKEW_Y;
 import static android.graphics.Matrix.MTRANS_X;
 import static android.graphics.Matrix.MTRANS_Y;
-import static android.view.Display.INVALID_DISPLAY;
 import static android.view.SurfaceControlProto.HASH_CODE;
 import static android.view.SurfaceControlProto.LAYER_ID;
 import static android.view.SurfaceControlProto.NAME;
@@ -38,7 +37,6 @@
 import android.annotation.Size;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
 import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.graphics.Matrix;
@@ -53,13 +51,8 @@
 import android.hardware.OverlayProperties;
 import android.hardware.SyncFence;
 import android.hardware.display.DeviceProductInfo;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerGlobal;
 import android.hardware.display.DisplayedContentSample;
 import android.hardware.display.DisplayedContentSamplingAttributes;
-import android.hardware.display.IDisplayManager;
-import android.hardware.display.IVirtualDisplayCallback;
-import android.hardware.display.VirtualDisplay;
 import android.hardware.graphics.common.DisplayDecorationSupport;
 import android.opengl.EGLDisplay;
 import android.opengl.EGLSync;
@@ -68,8 +61,6 @@
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
@@ -2355,92 +2346,6 @@
     }
 
     /**
-     * Because this API is now going through {@link DisplayManager}, orientation and displayRect
-     * will automatically be computed based on configuration changes. Because of this, the params
-     * orientation and displayRect are ignored
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
-            publicAlternatives = "Use {@code VirtualDisplay#resize(int, int, int)} instead.",
-            trackingBug = 247078497)
-    public static void setDisplayProjection(IBinder displayToken, int orientation,
-            Rect layerStackRect, Rect displayRect) {
-        DisplayManagerGlobal.getInstance().resizeVirtualDisplay(
-                IVirtualDisplayCallback.Stub.asInterface(displayToken), layerStackRect.width(),
-                layerStackRect.height(), 1);
-    }
-
-    /**
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
-            publicAlternatives = "Use {@code MediaProjection#createVirtualDisplay()} with flag "
-                    + " {@code VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR} for mirroring instead.",
-            trackingBug = 247078497)
-    public static void setDisplayLayerStack(IBinder displayToken, int layerStack) {
-        IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
-        if (b == null) {
-            throw new UnsupportedOperationException();
-        }
-
-        IDisplayManager dm = IDisplayManager.Stub.asInterface(b);
-        try {
-            dm.setDisplayIdToMirror(displayToken, layerStack);
-        } catch (RemoteException e) {
-            throw new UnsupportedOperationException(e);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
-            publicAlternatives = "Use {@code VirtualDisplay#setSurface(Surface)} instead.",
-            trackingBug = 247078497)
-    public static void setDisplaySurface(IBinder displayToken, Surface surface) {
-        IVirtualDisplayCallback virtualDisplayCallback =
-                IVirtualDisplayCallback.Stub.asInterface(displayToken);
-        DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
-        dm.setVirtualDisplaySurface(virtualDisplayCallback, surface);
-    }
-
-    /**
-     * Secure is no longer supported because this is only called from outside system which cannot
-     * create secure displays.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
-            publicAlternatives = "Use {@code MediaProjection#createVirtualDisplay()} or "
-                    + "{@code DisplayManager#createVirtualDisplay()} instead.",
-            trackingBug = 247078497)
-    public static IBinder createDisplay(String name, boolean secure) {
-        if (name == null) {
-            throw new IllegalArgumentException("name must not be null");
-        }
-
-        // We don't have a size yet so pass in 1 for width and height since 0 is invalid
-        VirtualDisplay vd = DisplayManager.createVirtualDisplay(name, 1 /* width */, 1 /* height */,
-                INVALID_DISPLAY, null /* Surface */);
-        return vd == null ? null : vd.getToken().asBinder();
-    }
-
-    /**
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
-            publicAlternatives = "Use {@code VirtualDisplay#release()} instead.",
-            trackingBug = 247078497)
-    public static void destroyDisplay(IBinder displayToken) {
-        if (displayToken == null) {
-            throw new IllegalArgumentException("displayToken must not be null");
-        }
-
-        DisplayManagerGlobal.getInstance().releaseVirtualDisplay(
-                IVirtualDisplayCallback.Stub.asInterface(displayToken));
-    }
-
-    /**
      * Returns whether protected content is supported in GPU composition.
      * @hide
      */
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 487b15c..1f81a64 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -235,6 +235,7 @@
 import com.android.internal.view.BaseSurfaceHolder;
 import com.android.internal.view.RootViewSurfaceTaker;
 import com.android.internal.view.SurfaceCallbackHelper;
+import com.android.modules.expresslog.Counter;
 import com.android.window.flags.Flags;
 
 import java.io.IOException;
@@ -9704,6 +9705,9 @@
             } else {
                 q.mReceiver.finishInputEvent(q.mEvent, handled);
             }
+            if (q.mEvent instanceof KeyEvent) {
+                logHandledSystemKey((KeyEvent) q.mEvent, handled);
+            }
         } else {
             q.mEvent.recycleIfNeededAfterDispatch();
         }
@@ -9711,6 +9715,19 @@
         recycleQueuedInputEvent(q);
     }
 
+    private void logHandledSystemKey(KeyEvent event, boolean handled) {
+        final int keyCode = event.getKeyCode();
+        if (keyCode != KeyEvent.KEYCODE_STEM_PRIMARY) {
+            return;
+        }
+        if (event.isDown() && event.getRepeatCount() == 0 && handled) {
+            // Initial DOWN event is handled. Log the stem primary key press.
+            Counter.logIncrementWithUid(
+                    "input.value_app_handled_stem_primary_key_gestures_count",
+                    Process.myUid());
+        }
+    }
+
     static boolean isTerminalInputEvent(InputEvent event) {
         if (event instanceof KeyEvent) {
             final KeyEvent keyEvent = (KeyEvent)event;
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 57a4161..921afaa 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -38,6 +38,8 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.graphics.Insets;
@@ -1519,6 +1521,9 @@
         }
 
         /** @hide */
+        @TestApi
+        @NonNull
+        @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
         public static String toString(@InsetsType int types) {
             StringBuilder result = new StringBuilder();
             if ((types & STATUS_BARS) != 0) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 53aed49..49a2843 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -5400,13 +5400,10 @@
         public static final AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT =
                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
 
+        // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
         /**
          * Action to scroll the node content forward.
          *
-         * <p>
-         *     <strong>Arguments:</strong>
-         *     {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
-         * </p>
          * <p>The UI element that implements this should send a
          * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. Depending on the orientation,
          * this element should also add the relevant directional scroll actions of
@@ -5447,12 +5444,10 @@
         public static final AccessibilityAction ACTION_SCROLL_FORWARD =
                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
 
+        // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
         /**
          * Action to scroll the node content backward.
-         * <p>
-         *     <strong>Arguments:</strong>
-         *     {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
-         * </p>
+         *
          * <p>The UI element that implements this should send a
          * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. Depending on the orientation,
          * this element should also add the relevant directional scroll actions of
@@ -5647,48 +5642,40 @@
         @NonNull public static final AccessibilityAction ACTION_SCROLL_IN_DIRECTION =
                 new AccessibilityAction(R.id.accessibilityActionScrollInDirection);
 
+        // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
         /**
          * Action to scroll the node content up.
-         * <p>
-         *     <strong>Arguments:</strong>
-         *     {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
-         * </p>
+         *
          * <p>The UI element that implements this should send a
          * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
          */
         public static final AccessibilityAction ACTION_SCROLL_UP =
                 new AccessibilityAction(R.id.accessibilityActionScrollUp);
 
+        // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
         /**
          * Action to scroll the node content left.
-         * <p>
-         *     <strong>Arguments:</strong>
-         *     {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
-         * </p>
+         *
          * <p>The UI element that implements this should send a
          * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
          */
         public static final AccessibilityAction ACTION_SCROLL_LEFT =
                 new AccessibilityAction(R.id.accessibilityActionScrollLeft);
 
+        // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
         /**
          * Action to scroll the node content down.
-         * <p>
-         *     <strong>Arguments:</strong>
-         *     {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
-         * </p>
+         *
          * <p>The UI element that implements this should send a
          * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
          */
         public static final AccessibilityAction ACTION_SCROLL_DOWN =
                 new AccessibilityAction(R.id.accessibilityActionScrollDown);
 
+        // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
         /**
          * Action to scroll the node content right.
-         * <p>
-         *     <strong>Arguments:</strong>
-         *     {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
-         * </p>
+         *
          * <p>The UI element that implements this should send a
          * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
          */
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index b3359b7..70d8abe 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -23,9 +23,6 @@
 import android.annotation.FlaggedApi;
 import android.annotation.InterpolatorRes;
 import android.annotation.TestApi;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
-import android.compat.annotation.Overridable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
@@ -55,18 +52,6 @@
     private static final int TOGETHER = 0;
     private static final int SEQUENTIALLY = 1;
 
-     /**
-     * For apps targeting {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} and above,
-     * this change ID enables to use expectedPresentationTime instead of the frameTime
-     * for the frame start time .
-     *
-     * @hide
-     */
-    @ChangeId
-    @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM)
-    @Overridable
-    public static final long OVERRIDE_ENABLE_EXPECTED_PRSENTATION_TIME = 278730197L;
-
     private static boolean sExpectedPresentationTimeFlagValue;
     static {
         sExpectedPresentationTimeFlagValue = expectedPresentationTimeReadOnly();
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index c7609a6..828ec26 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -44,9 +44,7 @@
     static final String PROXY_BASE = "file:///cookieless_proxy/";
     static final String CONTENT_BASE = "content:";
 
-    /**
-     * Cleans up (if possible) user-entered web addresses
-     */
+    /** Cleans up (if possible) user-entered web addresses */
     public static String guessUrl(String inUrl) {
 
         String retVal = inUrl;
@@ -86,8 +84,12 @@
         return webAddress.toString();
     }
 
-    public static String composeSearchUrl(String inQuery, String template,
-                                          String queryPlaceHolder) {
+    /**
+     * Inserts the {@code inQuery} in the {@code template} after URL-encoding it. The encoded query
+     * will replace the {@code queryPlaceHolder}.
+     */
+    public static String composeSearchUrl(
+            String inQuery, String template, String queryPlaceHolder) {
         int placeHolderIndex = template.indexOf(queryPlaceHolder);
         if (placeHolderIndex < 0) {
             return null;
@@ -104,8 +106,7 @@
             return null;
         }
 
-        buffer.append(template.substring(
-                placeHolderIndex + queryPlaceHolder.length()));
+        buffer.append(template.substring(placeHolderIndex + queryPlaceHolder.length()));
 
         return buffer.toString();
     }
@@ -123,8 +124,7 @@
             byte b = url[i];
             if (b == '%') {
                 if (url.length - i > 2) {
-                    b = (byte) (parseHex(url[i + 1]) * 16
-                            + parseHex(url[i + 2]));
+                    b = (byte) (parseHex(url[i + 1]) * 16 + parseHex(url[i + 2]));
                     i += 2;
                 } else {
                     throw new IllegalArgumentException("Invalid format");
@@ -189,8 +189,8 @@
     }
 
     /**
-     * @return {@code true} if the url is a proxy url to allow cookieless network
-     * requests from a file url.
+     * @return {@code true} if the url is a proxy url to allow cookieless network requests from a
+     *     file url.
      * @deprecated Cookieless proxy is no longer supported.
      */
     @Deprecated
@@ -202,9 +202,10 @@
      * @return {@code true} if the url is a local file.
      */
     public static boolean isFileUrl(String url) {
-        return (null != url) && (url.startsWith(FILE_BASE) &&
-                                 !url.startsWith(ASSET_BASE) &&
-                                 !url.startsWith(PROXY_BASE));
+        return (null != url)
+                && (url.startsWith(FILE_BASE)
+                        && !url.startsWith(ASSET_BASE)
+                        && !url.startsWith(PROXY_BASE));
     }
 
     /**
@@ -232,18 +233,18 @@
      * @return {@code true} if the url is an http: url.
      */
     public static boolean isHttpUrl(String url) {
-        return (null != url) &&
-               (url.length() > 6) &&
-               url.substring(0, 7).equalsIgnoreCase("http://");
+        return (null != url)
+                && (url.length() > 6)
+                && url.substring(0, 7).equalsIgnoreCase("http://");
     }
 
     /**
      * @return {@code true} if the url is an https: url.
      */
     public static boolean isHttpsUrl(String url) {
-        return (null != url) &&
-               (url.length() > 7) &&
-               url.substring(0, 8).equalsIgnoreCase("https://");
+        return (null != url)
+                && (url.length() > 7)
+                && url.substring(0, 8).equalsIgnoreCase("https://");
     }
 
     /**
@@ -271,19 +272,17 @@
             return false;
         }
 
-        return (isAssetUrl(url) ||
-                isResourceUrl(url) ||
-                isFileUrl(url) ||
-                isAboutUrl(url) ||
-                isHttpUrl(url) ||
-                isHttpsUrl(url) ||
-                isJavaScriptUrl(url) ||
-                isContentUrl(url));
+        return (isAssetUrl(url)
+                || isResourceUrl(url)
+                || isFileUrl(url)
+                || isAboutUrl(url)
+                || isHttpUrl(url)
+                || isHttpsUrl(url)
+                || isJavaScriptUrl(url)
+                || isContentUrl(url));
     }
 
-    /**
-     * Strips the url of the anchor.
-     */
+    /** Strips the url of the anchor. */
     public static String stripAnchor(String url) {
         int anchorIndex = url.indexOf('#');
         if (anchorIndex != -1) {
@@ -293,19 +292,16 @@
     }
 
     /**
-     * Guesses canonical filename that a download would have, using
-     * the URL and contentDisposition. File extension, if not defined,
-     * is added based on the mimetype
+     * Guesses canonical filename that a download would have, using the URL and contentDisposition.
+     * File extension, if not defined, is added based on the mimetype
+     *
      * @param url Url to the content
      * @param contentDisposition Content-Disposition HTTP header or {@code null}
      * @param mimeType Mime-type of the content or {@code null}
-     *
      * @return suggested filename
      */
     public static final String guessFileName(
-            String url,
-            @Nullable String contentDisposition,
-            @Nullable String mimeType) {
+            String url, @Nullable String contentDisposition, @Nullable String mimeType) {
         String filename = null;
         String extension = null;
 
@@ -369,8 +365,9 @@
                 // Compare the last segment of the extension against the mime type.
                 // If there's a mismatch, discard the entire extension.
                 int lastDotIndex = filename.lastIndexOf('.');
-                String typeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
-                        filename.substring(lastDotIndex + 1));
+                String typeFromExt =
+                        MimeTypeMap.getSingleton()
+                                .getMimeTypeFromExtension(filename.substring(lastDotIndex + 1));
                 if (typeFromExt != null && !typeFromExt.equalsIgnoreCase(mimeType)) {
                     extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
                     if (extension != null) {
@@ -389,17 +386,17 @@
 
     /** Regex used to parse content-disposition headers */
     private static final Pattern CONTENT_DISPOSITION_PATTERN =
-            Pattern.compile("attachment;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\1\\s*$",
-            Pattern.CASE_INSENSITIVE);
+            Pattern.compile(
+                    "attachment;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\1\\s*$",
+                    Pattern.CASE_INSENSITIVE);
 
     /**
-     * Parse the Content-Disposition HTTP Header. The format of the header
-     * is defined here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html
-     * This header provides a filename for content that is going to be
-     * downloaded to the file system. We only support the attachment type.
-     * Note that RFC 2616 specifies the filename value must be double-quoted.
-     * Unfortunately some servers do not quote the value so to maintain
-     * consistent behaviour with other browsers, we allow unquoted values too.
+     * Parse the Content-Disposition HTTP Header. The format of the header is defined here:
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html This header provides a filename for
+     * content that is going to be downloaded to the file system. We only support the attachment
+     * type. Note that RFC 2616 specifies the filename value must be double-quoted. Unfortunately
+     * some servers do not quote the value so to maintain consistent behaviour with other browsers,
+     * we allow unquoted values too.
      */
     @UnsupportedAppUsage
     static String parseContentDisposition(String contentDisposition) {
@@ -409,7 +406,7 @@
                 return m.group(2);
             }
         } catch (IllegalStateException ex) {
-             // This function is defined as returning null when it can't parse the header
+            // This function is defined as returning null when it can't parse the header
         }
         return null;
     }
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 14c5348..d12eda3 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1203,7 +1203,11 @@
      * changes to this setting after that point.
      *
      * @param flag {@code true} if the WebView should use the database storage API
+     * @deprecated WebSQL is deprecated and this method will become a no-op on all
+     * Android versions once support is removed in Chromium. See
+     * https://developer.chrome.com/blog/deprecating-web-sql for more information.
      */
+    @Deprecated
     public abstract void setDatabaseEnabled(boolean flag);
 
     /**
@@ -1236,7 +1240,11 @@
      *
      * @return {@code true} if the database storage API is enabled
      * @see #setDatabaseEnabled
+     * @deprecated WebSQL is deprecated and this method will become a no-op on all
+     * Android versions once support is removed in Chromium. See
+     * https://developer.chrome.com/blog/deprecating-web-sql for more information.
      */
+    @Deprecated
     public abstract boolean getDatabaseEnabled();
 
     /**
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 052e2f2..d3f3af7 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -610,6 +610,9 @@
     // ringer mode.
     optional SettingProto mode_ringer = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
+    // This is an optional feature where ringer mode affects alarm stream as well
+    optional SettingProto mute_alarm_stream_with_ringer_mode = 160 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
     reserved 147; // Used to be apply_ramping_ringer
 
     message MultiSim {
@@ -1086,5 +1089,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 160;
+    // Next tag = 161;
 }
diff --git a/core/proto/android/view/insetssource.proto b/core/proto/android/view/insetssource.proto
index e6c6d59..118dfc8 100644
--- a/core/proto/android/view/insetssource.proto
+++ b/core/proto/android/view/insetssource.proto
@@ -26,7 +26,7 @@
  * Represents a {@link android.view.InsetsSource} object.
  */
 message InsetsSourceProto {
-    optional string type = 1;
+    optional string type = 1 [deprecated=true];
     optional .android.graphics.RectProto frame = 2;
     optional .android.graphics.RectProto visible_frame = 3;
     optional bool visible = 4;
diff --git a/core/proto/android/view/insetssourceconsumer.proto b/core/proto/android/view/insetssourceconsumer.proto
index a01ad8e..882163f 100644
--- a/core/proto/android/view/insetssourceconsumer.proto
+++ b/core/proto/android/view/insetssourceconsumer.proto
@@ -27,11 +27,12 @@
  * Represents a {@link android.view.InsetsSourceConsumer} object.
  */
 message InsetsSourceConsumerProto {
-    optional string internal_insets_type = 1;
+    optional string internal_insets_type = 1 [deprecated=true];
     optional bool has_window_focus = 2;
     optional bool is_requested_visible = 3;
     optional InsetsSourceControlProto source_control = 4;
     optional .android.graphics.RectProto pending_frame = 5;
     optional .android.graphics.RectProto pending_visible_frame = 6;
     optional int32 animation_state = 7;
+    optional int32 type_number = 8;
 }
\ No newline at end of file
diff --git a/core/proto/android/view/insetssourcecontrol.proto b/core/proto/android/view/insetssourcecontrol.proto
index 3ac3cbf..afab57f 100644
--- a/core/proto/android/view/insetssourcecontrol.proto
+++ b/core/proto/android/view/insetssourcecontrol.proto
@@ -27,7 +27,8 @@
  * Represents a {@link android.view.InsetsSourceControl} object.
  */
 message InsetsSourceControlProto {
-    optional string type = 1;
+    optional string type = 1 [deprecated=true];
     optional .android.graphics.PointProto position = 2;
     optional SurfaceControlProto leash = 3;
+    optional int32 type_number = 4;
 }
\ No newline at end of file
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 596cfe5..d1143c4 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2986,7 +2986,12 @@
              depends on the number of isolated services that an application starts,
              and how much memory those services save by preloading and sharing memory with
              the app zygote. Therefore, it is recommended to measure memory usage under
-             typical workloads to determine whether it makes sense to use this flag. -->
+             typical workloads to determine whether it makes sense to use this flag.
+
+             <p>There is a limit to the number of isolated services that can be spawned from
+                the Application Zygote; the absolute limit is 100, but due to potential
+                delays in service process cleanup, a much safer limit to use in practice is 50.
+             -->
         <attr name="useAppZygote" format="boolean" />
         <!-- If this is a foreground service, specify its category. -->
         <attr name="foregroundServiceType" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d0de5f0..804e9ef 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2262,6 +2262,9 @@
     <!-- The default min volume for the alarm stream -->
     <integer name="config_audio_alarm_min_vol">1</integer>
 
+    <!-- Flag indicating if ringer mode affects alarm stream -->
+    <bool name="config_audio_ringer_mode_affects_alarm_stream">false</bool>
+
     <!-- The default value for whether head tracking for
          spatial audio is enabled for a newly connected audio device -->
     <bool name="config_spatial_audio_head_tracking_enabled_default">false</bool>
@@ -6862,4 +6865,8 @@
 
     <!-- Whether the media player is shown on the quick settings -->
     <bool name="config_quickSettingsShowMediaPlayer">true</bool>
+
+    <!-- Defines suitability of the built-in speaker route.
+         Refer to {@link MediaRoute2Info} to see supported values.  -->
+    <integer name="config_mediaRouter_builtInSpeakerSuitability">0</integer>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3894330..9589fb0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -288,6 +288,7 @@
   <java-symbol type="integer" name="config_audio_ring_vol_default" />
   <java-symbol type="integer" name="config_audio_ring_vol_steps" />
   <java-symbol type="integer" name="config_audio_alarm_min_vol" />
+  <java-symbol type="bool" name="config_audio_ringer_mode_affects_alarm_stream" />
   <java-symbol type="bool" name="config_spatial_audio_head_tracking_enabled_default" />
   <java-symbol type="bool" name="config_avoidGfxAccel" />
   <java-symbol type="bool" name="config_bluetooth_address_validation" />
@@ -5306,4 +5307,7 @@
   <java-symbol type="bool" name="config_viewBasedRotaryEncoderHapticsEnabled" />
 
   <java-symbol type="bool" name="config_quickSettingsShowMediaPlayer" />
+
+  <!-- Android MediaRouter framework configs. -->
+  <java-symbol type="integer" name="config_mediaRouter_builtInSpeakerSuitability" />
 </resources>
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 da87339..afd554b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -360,9 +360,7 @@
         if (activities == null) {
             return null;
         }
-        // Already checked nullity in collectNonFinishingActivities.
-        final Rect bounds = getInfo().getConfiguration().windowConfiguration.getBounds();
-        return new ActivityStack(activities, isEmpty(), mToken, bounds, mOverlayTag);
+        return new ActivityStack(activities, isEmpty(), mToken, mOverlayTag);
     }
 
     /** Adds the activity that will be reparented to this container. */
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 4511f3b..901d5fa 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -57,3 +57,10 @@
     description: "Enables left/right split in portrait"
     bug: "291018646"
 }
+
+flag {
+    name: "enable_new_bubble_animations"
+    namespace: "multitasking"
+    description: "Enables new animations for expand and collapse for bubbles"
+    bug: "311450609"
+}
diff --git a/media/java/android/media/IMediaRouter2.aidl b/media/java/android/media/IMediaRouter2.aidl
index 29bfd1a..e2dddad 100644
--- a/media/java/android/media/IMediaRouter2.aidl
+++ b/media/java/android/media/IMediaRouter2.aidl
@@ -19,6 +19,7 @@
 import android.media.MediaRoute2Info;
 import android.media.RoutingSessionInfo;
 import android.os.Bundle;
+import android.os.UserHandle;
 
 /**
  * @hide
@@ -35,5 +36,6 @@
      * Call MediaRouterService#requestCreateSessionWithRouter2 to pass the result.
      */
     void requestCreateSessionByManager(long uniqueRequestId, in RoutingSessionInfo oldSession,
-        in MediaRoute2Info route);
+        in MediaRoute2Info route, in UserHandle transferInitiatorUserHandle,
+        in String transferInitiatorPackageName);
 }
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index fa4d1a1..04e99ea 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -64,7 +64,8 @@
 
     void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId, long managerRequestId,
             in RoutingSessionInfo oldSession, in MediaRoute2Info route,
-            in @nullable Bundle sessionHints);
+            in @nullable Bundle sessionHints, in UserHandle transferInitiatorUserHandle,
+            in String transferInitiatorPackageName);
     void selectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route);
     void deselectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route);
     void transferToRouteWithRouter2(IMediaRouter2 router, String sessionId,
@@ -84,13 +85,16 @@
     void stopScan(IMediaRouter2Manager manager);
 
     void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId,
-            in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route);
+            in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route,
+            in UserHandle transferInitiatorUserHandle, in String transferInitiatorPackageName);
     void selectRouteWithManager(IMediaRouter2Manager manager, int requestId,
             String sessionId, in MediaRoute2Info route);
     void deselectRouteWithManager(IMediaRouter2Manager manager, int requestId,
             String sessionId, in MediaRoute2Info route);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)")
     void transferToRouteWithManager(IMediaRouter2Manager manager, int requestId,
-            String sessionId, in MediaRoute2Info route);
+            String sessionId, in MediaRoute2Info route,
+            in UserHandle transferInitiatorUserHandle, String transferInitiatorPackageName);
     void setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId,
             String sessionId, int volume);
     void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, String sessionId);
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 8ad3587..0eabe66 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -19,6 +19,7 @@
 import static android.media.MediaRouter2Utils.toUniqueId;
 
 import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
+import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
 import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES;
 
 import android.annotation.FlaggedApi;
@@ -479,6 +480,37 @@
     public static final String FEATURE_REMOTE_GROUP_PLAYBACK =
             "android.media.route.feature.REMOTE_GROUP_PLAYBACK";
 
+    /** Indicates the route is always suitable for media playback. */
+    @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+    public static final int SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER = 0;
+
+    /**
+     * Indicates that the route is suitable for media playback only after explicit user selection.
+     */
+    @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+    public static final int SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER = 1;
+
+    /** Indicates that the route is never suitable for media playback. */
+    @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+    public static final int SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER = 2;
+
+    /**
+     * Route suitability status.
+     *
+     * <p>Signals whether the route is suitable to play media.
+     *
+     * @hide
+     */
+    @IntDef(
+            value = {
+                SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER,
+                SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER,
+                SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+    public @interface SuitabilityStatus {}
+
     private final String mId;
     private final CharSequence mName;
     private final List<String> mFeatures;
@@ -500,6 +532,7 @@
     private final String mProviderId;
     private final boolean mIsVisibilityRestricted;
     private final Set<String> mAllowedPackages;
+    @SuitabilityStatus private final int mSuitabilityStatus;
 
     MediaRoute2Info(@NonNull Builder builder) {
         mId = builder.mId;
@@ -521,6 +554,7 @@
         mProviderId = builder.mProviderId;
         mIsVisibilityRestricted = builder.mIsVisibilityRestricted;
         mAllowedPackages = builder.mAllowedPackages;
+        mSuitabilityStatus = builder.mSuitabilityStatus;
     }
 
     MediaRoute2Info(@NonNull Parcel in) {
@@ -544,6 +578,7 @@
         mProviderId = in.readString();
         mIsVisibilityRestricted = in.readBoolean();
         mAllowedPackages = Set.of(in.createString8Array());
+        mSuitabilityStatus = in.readInt();
     }
 
     /**
@@ -778,6 +813,13 @@
                 || mAllowedPackages.contains(packageName);
     }
 
+    /** Returns the route suitability status. */
+    @SuitabilityStatus
+    @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+    public int getSuitabilityStatus() {
+        return mSuitabilityStatus;
+    }
+
     /**
      * Dumps the current state of the object to the given {@code pw} as a human-readable string.
      *
@@ -809,6 +851,7 @@
         pw.println(indent + "mProviderId=" + mProviderId);
         pw.println(indent + "mIsVisibilityRestricted=" + mIsVisibilityRestricted);
         pw.println(indent + "mAllowedPackages=" + mAllowedPackages);
+        pw.println(indent + "mSuitabilityStatus=" + mSuitabilityStatus);
     }
 
     private void dumpVolume(@NonNull PrintWriter pw, @NonNull String prefix) {
@@ -861,39 +904,74 @@
                 && Objects.equals(mDeduplicationIds, other.mDeduplicationIds)
                 && Objects.equals(mProviderId, other.mProviderId)
                 && (mIsVisibilityRestricted == other.mIsVisibilityRestricted)
-                && Objects.equals(mAllowedPackages, other.mAllowedPackages);
+                && Objects.equals(mAllowedPackages, other.mAllowedPackages)
+                && mSuitabilityStatus == other.mSuitabilityStatus;
     }
 
     @Override
     public int hashCode() {
         // Note: mExtras is not included.
-        return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription,
-                mConnectionState, mClientPackageName, mPackageName, mVolumeHandling, mVolumeMax,
-                mVolume, mAddress, mDeduplicationIds, mProviderId, mIsVisibilityRestricted,
-                mAllowedPackages);
+        return Objects.hash(
+                mId,
+                mName,
+                mFeatures,
+                mType,
+                mIsSystem,
+                mIconUri,
+                mDescription,
+                mConnectionState,
+                mClientPackageName,
+                mPackageName,
+                mVolumeHandling,
+                mVolumeMax,
+                mVolume,
+                mAddress,
+                mDeduplicationIds,
+                mProviderId,
+                mIsVisibilityRestricted,
+                mAllowedPackages,
+                mSuitabilityStatus);
     }
 
     @Override
     public String toString() {
         // Note: mExtras is not printed here.
-        StringBuilder result = new StringBuilder()
-                .append("MediaRoute2Info{ ")
-                .append("id=").append(getId())
-                .append(", name=").append(getName())
-                .append(", features=").append(getFeatures())
-                .append(", iconUri=").append(getIconUri())
-                .append(", description=").append(getDescription())
-                .append(", connectionState=").append(getConnectionState())
-                .append(", clientPackageName=").append(getClientPackageName())
-                .append(", volumeHandling=").append(getVolumeHandling())
-                .append(", volumeMax=").append(getVolumeMax())
-                .append(", volume=").append(getVolume())
-                .append(", address=").append(getAddress())
-                .append(", deduplicationIds=").append(String.join(",", getDeduplicationIds()))
-                .append(", providerId=").append(getProviderId())
-                .append(", isVisibilityRestricted=").append(mIsVisibilityRestricted)
-                .append(", allowedPackages=").append(String.join(",", mAllowedPackages))
-                .append(" }");
+        StringBuilder result =
+                new StringBuilder()
+                        .append("MediaRoute2Info{ ")
+                        .append("id=")
+                        .append(getId())
+                        .append(", name=")
+                        .append(getName())
+                        .append(", features=")
+                        .append(getFeatures())
+                        .append(", iconUri=")
+                        .append(getIconUri())
+                        .append(", description=")
+                        .append(getDescription())
+                        .append(", connectionState=")
+                        .append(getConnectionState())
+                        .append(", clientPackageName=")
+                        .append(getClientPackageName())
+                        .append(", volumeHandling=")
+                        .append(getVolumeHandling())
+                        .append(", volumeMax=")
+                        .append(getVolumeMax())
+                        .append(", volume=")
+                        .append(getVolume())
+                        .append(", address=")
+                        .append(getAddress())
+                        .append(", deduplicationIds=")
+                        .append(String.join(",", getDeduplicationIds()))
+                        .append(", providerId=")
+                        .append(getProviderId())
+                        .append(", isVisibilityRestricted=")
+                        .append(mIsVisibilityRestricted)
+                        .append(", allowedPackages=")
+                        .append(String.join(",", mAllowedPackages))
+                        .append(", suitabilityStatus=")
+                        .append(mSuitabilityStatus)
+                        .append(" }");
         return result.toString();
     }
 
@@ -923,6 +1001,7 @@
         dest.writeString(mProviderId);
         dest.writeBoolean(mIsVisibilityRestricted);
         dest.writeString8Array(mAllowedPackages.toArray(new String[0]));
+        dest.writeInt(mSuitabilityStatus);
     }
 
     private static String getDeviceTypeString(@Type int deviceType) {
@@ -1005,6 +1084,7 @@
         private String mProviderId;
         private boolean mIsVisibilityRestricted;
         private Set<String> mAllowedPackages;
+        @SuitabilityStatus private int mSuitabilityStatus;
 
         /**
          * Constructor for builder to create {@link MediaRoute2Info}.
@@ -1028,6 +1108,7 @@
             mFeatures = new ArrayList<>();
             mDeduplicationIds = Set.of();
             mAllowedPackages = Set.of();
+            mSuitabilityStatus = SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
         }
 
         /**
@@ -1075,6 +1156,7 @@
             mProviderId = routeInfo.mProviderId;
             mIsVisibilityRestricted = routeInfo.mIsVisibilityRestricted;
             mAllowedPackages = routeInfo.mAllowedPackages;
+            mSuitabilityStatus = routeInfo.mSuitabilityStatus;
         }
 
         /**
@@ -1318,6 +1400,23 @@
         }
 
         /**
+         * Sets route suitability status.
+         *
+         * <p>The default value is {@link
+         * MediaRoute2Info#SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER}.
+         *
+         * <p> Apps are not supposed to set {@link
+         * MediaRoute2Info#SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER}. Publishing a non-system
+         * route with such status throws {@link SecurityException}.
+         */
+        @NonNull
+        @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+        public Builder setSuitabilityStatus(@SuitabilityStatus int suitabilityStatus) {
+            mSuitabilityStatus = suitabilityStatus;
+            return this;
+        }
+
+        /**
          * Builds the {@link MediaRoute2Info media route info}.
          *
          * @throws IllegalArgumentException if no features are added.
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index ba26df9..5e23551 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
 import static com.android.media.flags.Flags.FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2;
 import static com.android.media.flags.Flags.FLAG_ENABLE_CROSS_USER_ROUTING_IN_MEDIA_ROUTER2;
 
@@ -699,15 +700,48 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+    @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)
     public void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) {
-        mImpl.transfer(controller.getRoutingSessionInfo(), route);
+        mImpl.transfer(
+                controller.getRoutingSessionInfo(),
+                route,
+                android.os.Process.myUserHandle(),
+                mContext.getPackageName());
+    }
+
+    /**
+     * Transfers the media of a routing controller to the given route.
+     *
+     * <p>This will be no-op for non-system media routers.
+     *
+     * @param controller a routing controller controlling media routing.
+     * @param route the route you want to transfer the media to.
+     * @param transferInitiatorUserHandle the user handle of the app that initiated the transfer
+     *     request.
+     * @param transferInitiatorPackageName the package name of the app that initiated the transfer.
+     *     This value is used with the user handle to populate {@link
+     *     RoutingController#wasTransferRequestedBySelf()}.
+     * @hide
+     */
+    @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+    public void transfer(
+            @NonNull RoutingController controller,
+            @NonNull MediaRoute2Info route,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName) {
+        mImpl.transfer(
+                controller.getRoutingSessionInfo(),
+                route,
+                transferInitiatorUserHandle,
+                transferInitiatorPackageName);
     }
 
     void requestCreateController(
             @NonNull RoutingController controller,
             @NonNull MediaRoute2Info route,
-            long managerRequestId) {
+            long managerRequestId,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName) {
 
         final int requestId = mNextRequestId.getAndIncrement();
 
@@ -736,7 +770,9 @@
                         managerRequestId,
                         controller.getRoutingSessionInfo(),
                         route,
-                        controllerHints);
+                        controllerHints,
+                        transferInitiatorUserHandle,
+                        transferInitiatorPackageName);
             } catch (RemoteException ex) {
                 Log.e(TAG, "createControllerForTransfer: "
                                 + "Failed to request for creating a controller.", ex);
@@ -1053,7 +1089,11 @@
     }
 
     void onRequestCreateControllerByManagerOnHandler(
-            RoutingSessionInfo oldSession, MediaRoute2Info route, long managerRequestId) {
+            RoutingSessionInfo oldSession,
+            MediaRoute2Info route,
+            long managerRequestId,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName) {
         Log.i(
                 TAG,
                 TextUtils.formatSimple(
@@ -1070,7 +1110,8 @@
         if (controller == null) {
             return;
         }
-        requestCreateController(controller, route, managerRequestId);
+        requestCreateController(controller, route, managerRequestId, transferInitiatorUserHandle,
+                transferInitiatorPackageName);
     }
 
     private List<MediaRoute2Info> getSortedRoutes(
@@ -1469,6 +1510,21 @@
         }
 
         /**
+         * Returns whether the transfer was requested by the calling app (as determined by comparing
+         * {@link UserHandle} and package name).
+         */
+        @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+        public boolean wasTransferRequestedBySelf() {
+            RoutingSessionInfo sessionInfo = getRoutingSessionInfo();
+
+            UserHandle transferInitiatorUserHandle = sessionInfo.getTransferInitiatorUserHandle();
+            String transferInitiatorPackageName = sessionInfo.getTransferInitiatorPackageName();
+
+            return Objects.equals(android.os.Process.myUserHandle(), transferInitiatorUserHandle)
+                    && Objects.equals(mContext.getPackageName(), transferInitiatorPackageName);
+        }
+
+        /**
          * Returns the current {@link RoutingSessionInfo} associated to this controller.
          */
         @NonNull
@@ -1980,14 +2036,20 @@
 
         @Override
         public void requestCreateSessionByManager(
-                long managerRequestId, RoutingSessionInfo oldSession, MediaRoute2Info route) {
+                long managerRequestId,
+                RoutingSessionInfo oldSession,
+                MediaRoute2Info route,
+                UserHandle transferInitiatorUserHandle,
+                String transferInitiatorPackageName) {
             mHandler.sendMessage(
                     obtainMessage(
                             MediaRouter2::onRequestCreateControllerByManagerOnHandler,
                             MediaRouter2.this,
                             oldSession,
                             route,
-                            managerRequestId));
+                            managerRequestId,
+                            transferInitiatorUserHandle,
+                            transferInitiatorPackageName));
         }
     }
 
@@ -2027,7 +2089,11 @@
 
         void stop();
 
-        void transfer(RoutingSessionInfo sessionInfo, MediaRoute2Info route);
+        void transfer(
+                @NonNull RoutingSessionInfo sessionInfo,
+                @NonNull MediaRoute2Info route,
+                @NonNull UserHandle transferInitiatorUserHandle,
+                @NonNull String transferInitiatorPackageName);
 
         List<RoutingController> getControllers();
 
@@ -2220,7 +2286,11 @@
 
             List<RoutingSessionInfo> sessionInfos = getRoutingSessions();
             RoutingSessionInfo targetSession = sessionInfos.get(sessionInfos.size() - 1);
-            transfer(targetSession, route);
+            transfer(
+                    targetSession,
+                    route,
+                    android.os.Process.myUserHandle(),
+                    mContext.getPackageName());
         }
 
         @Override
@@ -2243,14 +2313,24 @@
          *
          * @param sessionInfo The {@link RoutingSessionInfo routing session} to transfer.
          * @param route The {@link MediaRoute2Info route} to transfer to.
-         * @see #transferToRoute(RoutingSessionInfo, MediaRoute2Info)
+         * @param transferInitiatorUserHandle The user handle of the app that initiated the
+         *     transfer.
+         * @param transferInitiatorPackageName The package name if of the app that initiated the
+         *     transfer.
+         * @see #transferToRoute(RoutingSessionInfo, MediaRoute2Info, UserHandle, String)
          * @see #requestCreateSession(RoutingSessionInfo, MediaRoute2Info)
          */
         @Override
+        @SuppressWarnings("AndroidFrameworkRequiresPermission")
         public void transfer(
-                @NonNull RoutingSessionInfo sessionInfo, @NonNull MediaRoute2Info route) {
+                @NonNull RoutingSessionInfo sessionInfo,
+                @NonNull MediaRoute2Info route,
+                @NonNull UserHandle transferInitiatorUserHandle,
+                @NonNull String transferInitiatorPackageName) {
             Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
             Objects.requireNonNull(route, "route must not be null");
+            Objects.requireNonNull(transferInitiatorUserHandle);
+            Objects.requireNonNull(transferInitiatorPackageName);
 
             Log.v(
                     TAG,
@@ -2268,9 +2348,14 @@
             }
 
             if (sessionInfo.getTransferableRoutes().contains(route.getId())) {
-                transferToRoute(sessionInfo, route);
+                transferToRoute(
+                        sessionInfo,
+                        route,
+                        transferInitiatorUserHandle,
+                        transferInitiatorPackageName);
             } else {
-                requestCreateSession(sessionInfo, route);
+                requestCreateSession(sessionInfo, route, transferInitiatorUserHandle,
+                        transferInitiatorPackageName);
             }
         }
 
@@ -2282,21 +2367,30 @@
          * RoutingSessionInfo routing session's} {@link RoutingSessionInfo#getTransferableRoutes()
          * transferable routes list}. Otherwise, the request will fail.
          *
-         * <p>Use {@link #requestCreateSession(RoutingSessionInfo, MediaRoute2Info)} to request
-         * an out-of-session transfer.
+         * <p>Use {@link #requestCreateSession(RoutingSessionInfo, MediaRoute2Info)} to request an
+         * out-of-session transfer.
          *
          * @param session The {@link RoutingSessionInfo routing session} to transfer.
          * @param route The {@link MediaRoute2Info route} to transfer to. Must be one of the {@link
          *     RoutingSessionInfo routing session's} {@link
          *     RoutingSessionInfo#getTransferableRoutes() transferable routes}.
          */
+        @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
         private void transferToRoute(
-                @NonNull RoutingSessionInfo session, @NonNull MediaRoute2Info route) {
+                @NonNull RoutingSessionInfo session,
+                @NonNull MediaRoute2Info route,
+                @NonNull UserHandle transferInitiatorUserHandle,
+                @NonNull String transferInitiatorPackageName) {
             int requestId = createTransferRequest(session, route);
 
             try {
                 mMediaRouterService.transferToRouteWithManager(
-                        mClient, requestId, session.getId(), route);
+                        mClient,
+                        requestId,
+                        session.getId(),
+                        route,
+                        transferInitiatorUserHandle,
+                        transferInitiatorPackageName);
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
@@ -2317,7 +2411,10 @@
          * @param route The {@link MediaRoute2Info route} to transfer to.
          */
         private void requestCreateSession(
-                @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) {
+                @NonNull RoutingSessionInfo oldSession,
+                @NonNull MediaRoute2Info route,
+                @NonNull UserHandle transferInitiatorUserHandle,
+                @NonNull String transferInitiatorPackageName) {
             if (TextUtils.isEmpty(oldSession.getClientPackageName())) {
                 Log.w(TAG, "requestCreateSession: Can't create a session without package name.");
                 this.onTransferFailed(oldSession, route);
@@ -2328,7 +2425,12 @@
 
             try {
                 mMediaRouterService.requestCreateSessionWithManager(
-                        mClient, requestId, oldSession, route);
+                        mClient,
+                        requestId,
+                        oldSession,
+                        route,
+                        transferInitiatorUserHandle,
+                        transferInitiatorPackageName);
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
@@ -3055,7 +3157,8 @@
                 return;
             }
 
-            requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE);
+            requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE,
+                    android.os.Process.myUserHandle(), mContext.getPackageName());
         }
 
         @Override
@@ -3071,7 +3174,11 @@
          * #transferTo(MediaRoute2Info)}.
          */
         @Override
-        public void transfer(RoutingSessionInfo sessionInfo, MediaRoute2Info route) {
+        public void transfer(
+                @NonNull RoutingSessionInfo sessionInfo,
+                @NonNull MediaRoute2Info route,
+                @NonNull UserHandle transferInitiatorUserHandle,
+                @NonNull String transferInitiatorPackageName) {
             // Do nothing.
         }
 
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 830708c..06c0996 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -18,9 +18,11 @@
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
+import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.media.session.MediaController;
 import android.media.session.MediaSessionManager;
@@ -28,6 +30,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -467,30 +470,42 @@
      * <p>Same as {@link #transfer(RoutingSessionInfo, MediaRoute2Info)}, but resolves the routing
      * session based on the provided package name.
      */
-    public void transfer(@NonNull String packageName, @NonNull MediaRoute2Info route) {
+    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+    public void transfer(
+            @NonNull String packageName,
+            @NonNull MediaRoute2Info route,
+            @NonNull UserHandle userHandle) {
         Objects.requireNonNull(packageName, "packageName must not be null");
         Objects.requireNonNull(route, "route must not be null");
 
         List<RoutingSessionInfo> sessionInfos = getRoutingSessions(packageName);
         RoutingSessionInfo targetSession = sessionInfos.get(sessionInfos.size() - 1);
-        transfer(targetSession, route);
+        transfer(targetSession, route, userHandle, packageName);
     }
 
     /**
      * Transfers a routing session to a media route.
+     *
      * <p>{@link Callback#onTransferred} or {@link Callback#onTransferFailed} will be called
      * depending on the result.
      *
      * @param sessionInfo the routing session info to transfer
      * @param route the route transfer to
-     *
+     * @param transferInitiatorUserHandle the user handle of an app initiated the transfer
+     * @param transferInitiatorPackageName the package name of an app initiated the transfer
      * @see Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)
      * @see Callback#onTransferFailed(RoutingSessionInfo, MediaRoute2Info)
      */
-    public void transfer(@NonNull RoutingSessionInfo sessionInfo,
-            @NonNull MediaRoute2Info route) {
+    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+    public void transfer(
+            @NonNull RoutingSessionInfo sessionInfo,
+            @NonNull MediaRoute2Info route,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName) {
         Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
         Objects.requireNonNull(route, "route must not be null");
+        Objects.requireNonNull(transferInitiatorUserHandle);
+        Objects.requireNonNull(transferInitiatorPackageName);
 
         Log.v(TAG, "Transferring routing session. session= " + sessionInfo + ", route=" + route);
 
@@ -503,9 +518,11 @@
         }
 
         if (sessionInfo.getTransferableRoutes().contains(route.getId())) {
-            transferToRoute(sessionInfo, route);
+            transferToRoute(
+                    sessionInfo, route, transferInitiatorUserHandle, transferInitiatorPackageName);
         } else {
-            requestCreateSession(sessionInfo, route);
+            requestCreateSession(sessionInfo, route, transferInitiatorUserHandle,
+                    transferInitiatorPackageName);
         }
     }
 
@@ -873,19 +890,30 @@
      *
      * @hide
      */
-    private void transferToRoute(@NonNull RoutingSessionInfo session,
-            @NonNull MediaRoute2Info route) {
+    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+    private void transferToRoute(
+            @NonNull RoutingSessionInfo session,
+            @NonNull MediaRoute2Info route,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName) {
         int requestId = createTransferRequest(session, route);
 
         try {
             mMediaRouterService.transferToRouteWithManager(
-                    mClient, requestId, session.getId(), route);
+                    mClient,
+                    requestId,
+                    session.getId(),
+                    route,
+                    transferInitiatorUserHandle,
+                    transferInitiatorPackageName);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
     }
 
-    private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route) {
+    private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiationPackageName) {
         if (TextUtils.isEmpty(oldSession.getClientPackageName())) {
             Log.w(TAG, "requestCreateSession: Can't create a session without package name.");
             notifyTransferFailed(oldSession, route);
@@ -896,7 +924,8 @@
 
         try {
             mMediaRouterService.requestCreateSessionWithManager(
-                    mClient, requestId, oldSession, route);
+                    mClient, requestId, oldSession, route, transferInitiatorUserHandle,
+                    transferInitiationPackageName);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index a77c943..d28c26d 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -16,18 +16,25 @@
 
 package android.media;
 
+import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.TextUtils;
 
 import com.android.internal.util.Preconditions;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -55,6 +62,33 @@
     private static final String KEY_GROUP_ROUTE = "androidx.mediarouter.media.KEY_GROUP_ROUTE";
     private static final String KEY_VOLUME_HANDLING = "volumeHandling";
 
+    /**
+     * Indicates that the transfer happened by the default logic without explicit system's or user's
+     * request.
+     *
+     * <p>For example, an automatically connected Bluetooth device will have this transfer reason.
+     */
+    @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+    public static final int TRANSFER_REASON_FALLBACK = 0;
+
+    /** Indicates that the transfer happened from within a privileged application. */
+    @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+    public static final int TRANSFER_REASON_SYSTEM_REQUEST = 1;
+
+    /** Indicates that the transfer happened from a non-privileged app. */
+    @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+    public static final int TRANSFER_REASON_APP = 2;
+
+    /**
+     * Indicates the transfer reason.
+     *
+     * @hide
+     */
+    @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+    @IntDef(value = {TRANSFER_REASON_FALLBACK, TRANSFER_REASON_SYSTEM_REQUEST, TRANSFER_REASON_APP})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TransferReason {}
+
     @NonNull
     final String mId;
     @Nullable
@@ -82,6 +116,10 @@
     final Bundle mControlHints;
     final boolean mIsSystemSession;
 
+    @TransferReason final int mTransferReason;
+
+    @Nullable final UserHandle mTransferInitiatorUserHandle;
+    @Nullable final String mTransferInitiatorPackageName;
 
     RoutingSessionInfo(@NonNull Builder builder) {
         Objects.requireNonNull(builder, "builder must not be null.");
@@ -116,6 +154,9 @@
                         volumeAdjustmentForRemoteGroupSessions);
 
         mControlHints = updateVolumeHandlingInHints(builder.mControlHints, mVolumeHandling);
+        mTransferReason = builder.mTransferReason;
+        mTransferInitiatorUserHandle = builder.mTransferInitiatorUserHandle;
+        mTransferInitiatorPackageName = builder.mTransferInitiatorPackageName;
     }
 
     RoutingSessionInfo(@NonNull Parcel src) {
@@ -140,6 +181,9 @@
 
         mControlHints = src.readBundle();
         mIsSystemSession = src.readBoolean();
+        mTransferReason = src.readInt();
+        mTransferInitiatorUserHandle = src.readParcelable(null, android.os.UserHandle.class);
+        mTransferInitiatorPackageName = src.readString();
     }
 
     @Nullable
@@ -330,6 +374,27 @@
         return mIsSystemSession;
     }
 
+    /** Returns the transfer reason for this routing session. */
+    @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+    @TransferReason
+    public int getTransferReason() {
+        return mTransferReason;
+    }
+
+    /** @hide */
+    @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+    @Nullable
+    public UserHandle getTransferInitiatorUserHandle() {
+        return mTransferInitiatorUserHandle;
+    }
+
+    /** @hide */
+    @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+    @Nullable
+    public String getTransferInitiatorPackageName() {
+        return mTransferInitiatorPackageName;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -351,6 +416,13 @@
         dest.writeInt(mVolume);
         dest.writeBundle(mControlHints);
         dest.writeBoolean(mIsSystemSession);
+        dest.writeInt(mTransferReason);
+        if (mTransferInitiatorUserHandle != null) {
+            mTransferInitiatorUserHandle.writeToParcel(dest, /* flags= */ 0);
+        } else {
+            dest.writeParcelable(null, /* flags= */ 0);
+        }
+        dest.writeString(mTransferInitiatorPackageName);
     }
 
     /**
@@ -379,6 +451,9 @@
         pw.println(indent + "mVolume=" + mVolume);
         pw.println(indent + "mControlHints=" + mControlHints);
         pw.println(indent + "mIsSystemSession=" + mIsSystemSession);
+        pw.println(indent + "mTransferReason=" + mTransferReason);
+        pw.println(indent + "mtransferInitiatorUserHandle=" + mTransferInitiatorUserHandle);
+        pw.println(indent + "mtransferInitiatorPackageName=" + mTransferInitiatorPackageName);
     }
 
     @Override
@@ -406,39 +481,69 @@
                 && Objects.equals(mTransferableRoutes, other.mTransferableRoutes)
                 && (mVolumeHandling == other.mVolumeHandling)
                 && (mVolumeMax == other.mVolumeMax)
-                && (mVolume == other.mVolume);
+                && (mVolume == other.mVolume)
+                && (mTransferReason == other.mTransferReason)
+                && Objects.equals(mTransferInitiatorUserHandle, other.mTransferInitiatorUserHandle)
+                && Objects.equals(
+                mTransferInitiatorPackageName, other.mTransferInitiatorPackageName);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mId, mName, mOwnerPackageName, mClientPackageName, mProviderId,
-                mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferableRoutes,
-                mVolumeMax, mVolumeHandling, mVolume);
+        return Objects.hash(
+                mId,
+                mName,
+                mOwnerPackageName,
+                mClientPackageName,
+                mProviderId,
+                mSelectedRoutes,
+                mSelectableRoutes,
+                mDeselectableRoutes,
+                mTransferableRoutes,
+                mVolumeMax,
+                mVolumeHandling,
+                mVolume,
+                mTransferReason,
+                mTransferInitiatorUserHandle,
+                mTransferInitiatorPackageName);
     }
 
     @Override
     public String toString() {
-        StringBuilder result = new StringBuilder()
-                .append("RoutingSessionInfo{ ")
-                .append("sessionId=").append(getId())
-                .append(", name=").append(getName())
-                .append(", clientPackageName=").append(getClientPackageName())
-                .append(", selectedRoutes={")
-                .append(String.join(",", getSelectedRoutes()))
-                .append("}")
-                .append(", selectableRoutes={")
-                .append(String.join(",", getSelectableRoutes()))
-                .append("}")
-                .append(", deselectableRoutes={")
-                .append(String.join(",", getDeselectableRoutes()))
-                .append("}")
-                .append(", transferableRoutes={")
-                .append(String.join(",", getTransferableRoutes()))
-                .append("}")
-                .append(", volumeHandling=").append(getVolumeHandling())
-                .append(", volumeMax=").append(getVolumeMax())
-                .append(", volume=").append(getVolume())
-                .append(" }");
+        StringBuilder result =
+                new StringBuilder()
+                        .append("RoutingSessionInfo{ ")
+                        .append("sessionId=")
+                        .append(getId())
+                        .append(", name=")
+                        .append(getName())
+                        .append(", clientPackageName=")
+                        .append(getClientPackageName())
+                        .append(", selectedRoutes={")
+                        .append(String.join(",", getSelectedRoutes()))
+                        .append("}")
+                        .append(", selectableRoutes={")
+                        .append(String.join(",", getSelectableRoutes()))
+                        .append("}")
+                        .append(", deselectableRoutes={")
+                        .append(String.join(",", getDeselectableRoutes()))
+                        .append("}")
+                        .append(", transferableRoutes={")
+                        .append(String.join(",", getTransferableRoutes()))
+                        .append("}")
+                        .append(", volumeHandling=")
+                        .append(getVolumeHandling())
+                        .append(", volumeMax=")
+                        .append(getVolumeMax())
+                        .append(", volume=")
+                        .append(getVolume())
+                        .append(", transferReason=")
+                        .append(getTransferReason())
+                        .append(", transferInitiatorUserHandle=")
+                        .append(getTransferInitiatorUserHandle())
+                        .append(", transferInitiatorPackageName=")
+                        .append(getTransferInitiatorPackageName())
+                        .append(" }");
         return result.toString();
     }
 
@@ -494,6 +599,9 @@
         @Nullable
         private Bundle mControlHints;
         private boolean mIsSystemSession;
+        @TransferReason private int mTransferReason = TRANSFER_REASON_FALLBACK;
+        @Nullable private UserHandle mTransferInitiatorUserHandle;
+        @Nullable private String mTransferInitiatorPackageName;
 
         /**
          * Constructor for builder to create {@link RoutingSessionInfo}.
@@ -555,6 +663,9 @@
 
             mControlHints = sessionInfo.mControlHints;
             mIsSystemSession = sessionInfo.mIsSystemSession;
+            mTransferReason = sessionInfo.mTransferReason;
+            mTransferInitiatorUserHandle = sessionInfo.mTransferInitiatorUserHandle;
+            mTransferInitiatorPackageName = sessionInfo.mTransferInitiatorPackageName;
         }
 
         /**
@@ -784,6 +895,35 @@
         }
 
         /**
+         * Sets transfer reason for the current session.
+         *
+         * <p>By default the transfer reason is set to {@link
+         * RoutingSessionInfo#TRANSFER_REASON_FALLBACK}.
+         */
+        @NonNull
+        @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+        public Builder setTransferReason(@TransferReason int transferReason) {
+            mTransferReason = transferReason;
+            return this;
+        }
+
+        /**
+         * Sets the user handle and package name of the process that initiated the transfer.
+         *
+         * <p>By default the transfer initiation user handle and package name are set to {@code
+         * null}.
+         */
+        @NonNull
+        @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+        public Builder setTransferInitiator(
+                @Nullable UserHandle transferInitiatorUserHandle,
+                @Nullable String transferInitiatorPackageName) {
+            mTransferInitiatorUserHandle = transferInitiatorUserHandle;
+            mTransferInitiatorPackageName = transferInitiatorPackageName;
+            return this;
+        }
+
+        /**
          * Builds a routing session info.
          *
          * @throws IllegalArgumentException if no selected routes are added.
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 07f63e5..3da52cc 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -69,3 +69,11 @@
     description: "Use BluetoothDevice.getAlias to populate the name of Bluetooth MediaRoute2Infos."
     bug: "314324170"
 }
+
+flag {
+     name: "enable_built_in_speaker_route_suitability_statuses"
+     namespace: "media_solutions"
+     description: "Make MediaRoute2Info provide information about routes suitability for transfer."
+     bug: "279555229"
+}
+
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 757e9f8..96e95fd 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -3696,7 +3696,7 @@
                     "([Landroid/media/tv/tuner/filter/FilterEvent;)V");
 
     jclass sharedFilterClazz = env->FindClass("android/media/tv/tuner/filter/SharedFilter");
-    gFields.sharedFilterContext = env->GetFieldID(filterClazz, "mNativeContext", "J");
+    gFields.sharedFilterContext = env->GetFieldID(sharedFilterClazz, "mNativeContext", "J");
     gFields.sharedFilterInitID = env->GetMethodID(sharedFilterClazz, "<init>", "()V");
     gFields.onSharedFilterStatusID = env->GetMethodID(sharedFilterClazz, "onFilterStatus", "(I)V");
     gFields.onSharedFilterEventID =
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 8ed4bf2..c836df3 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -385,7 +385,9 @@
         MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
         assertThat(routeToSelect).isNotNull();
 
-        mManager.transfer(mPackageName, routeToSelect);
+        mManager.transfer(
+                mPackageName, routeToSelect,
+                android.os.Process.myUserHandle());
         assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
         assertThat(mManager.getRemoteSessions()).hasSize(1);
     }
@@ -411,7 +413,9 @@
 
         assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
 
-        mManager.transfer(mPackageName, routeToSelect);
+        mManager.transfer(
+                mPackageName, routeToSelect,
+                android.os.Process.myUserHandle());
         assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
 
         List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
@@ -450,7 +454,11 @@
                 .addFeature(FEATURE_REMOTE_PLAYBACK)
                 .build();
 
-        mManager.transfer(mManager.getSystemRoutingSession(null), unknownRoute);
+        mManager.transfer(
+                mManager.getSystemRoutingSession(null),
+                unknownRoute,
+                android.os.Process.myUserHandle(),
+                mContext.getPackageName());
         assertThat(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)).isFalse();
         assertThat(onTransferFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
     }
@@ -484,7 +492,11 @@
         assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
         assertThat(mRouter2.getControllers()).hasSize(1);
 
-        mManager.transfer(mManager.getRoutingSessions(mPackageName).get(0), routeToSelect);
+        mManager.transfer(
+                mManager.getRoutingSessions(mPackageName).get(0),
+                routeToSelect,
+                android.os.Process.myUserHandle(),
+                mContext.getPackageName());
         assertThat(transferLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
 
         assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(2);
@@ -516,7 +528,11 @@
             }
         });
         awaitOnRouteChangedManager(
-                () -> mManager.transfer(mPackageName, routes.get(ROUTE_ID1)),
+                () ->
+                        mManager.transfer(
+                                mPackageName,
+                                routes.get(ROUTE_ID1),
+                                android.os.Process.myUserHandle()),
                 ROUTE_ID1,
                 route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
         assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
@@ -527,7 +543,11 @@
         RoutingSessionInfo sessionInfo = sessions.get(1);
 
         awaitOnRouteChangedManager(
-                () -> mManager.transfer(mPackageName, routes.get(ROUTE_ID5_TO_TRANSFER_TO)),
+                () ->
+                        mManager.transfer(
+                                mPackageName,
+                                routes.get(ROUTE_ID5_TO_TRANSFER_TO),
+                                android.os.Process.myUserHandle()),
                 ROUTE_ID5_TO_TRANSFER_TO,
                 route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
 
@@ -585,9 +605,11 @@
         assertThat(route1).isNotNull();
         assertThat(route2).isNotNull();
 
-        mManager.transfer(mPackageName, route1);
+        mManager.transfer(
+                mPackageName, route1, android.os.Process.myUserHandle());
         assertThat(successLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
-        mManager.transfer(mPackageName, route2);
+        mManager.transfer(
+                mPackageName, route2, android.os.Process.myUserHandle());
         assertThat(successLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
 
         // onTransferFailed/onSessionReleased should not be called.
@@ -634,7 +656,11 @@
 
         List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
         RoutingSessionInfo targetSession = sessions.get(sessions.size() - 1);
-        mManager.transfer(targetSession, routes.get(ROUTE_ID6_TO_BE_IGNORED));
+        mManager.transfer(
+                targetSession,
+                routes.get(ROUTE_ID6_TO_BE_IGNORED),
+                android.os.Process.myUserHandle(),
+                mContext.getPackageName());
 
         assertThat(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)).isFalse();
         assertThat(onFailedLatch.await(MediaRouter2Manager.TRANSFER_TIMEOUT_MS,
@@ -705,7 +731,10 @@
             }
         });
 
-        mManager.transfer(mPackageName, routes.get(ROUTE_ID1));
+        mManager.transfer(
+                mPackageName,
+                routes.get(ROUTE_ID1),
+                android.os.Process.myUserHandle());
         assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
 
         List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
@@ -860,7 +889,8 @@
         });
 
         mRouter2.setOnGetControllerHintsListener(listener);
-        mManager.transfer(mPackageName, route);
+        mManager.transfer(
+                mPackageName, route, android.os.Process.myUserHandle());
         assertThat(hintLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
         assertThat(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
 
@@ -905,7 +935,10 @@
             }
         });
 
-        mManager.transfer(mPackageName, routes.get(ROUTE_ID4_TO_SELECT_AND_DESELECT));
+        mManager.transfer(
+                mPackageName,
+                routes.get(ROUTE_ID4_TO_SELECT_AND_DESELECT),
+                android.os.Process.myUserHandle());
         assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
     }
 
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index c4c8128..abe4a3d 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -18,10 +18,12 @@
 
 #include <aidl/android/hardware/power/SessionHint.h>
 #include <aidl/android/hardware/power/SessionMode.h>
+#include <android-base/stringprintf.h>
 #include <android/WorkDuration.h>
 #include <android/os/IHintManager.h>
 #include <android/os/IHintSession.h>
 #include <android/performance_hint.h>
+#include <android/trace.h>
 #include <binder/Binder.h>
 #include <binder/IBinder.h>
 #include <binder/IServiceManager.h>
@@ -30,6 +32,7 @@
 #include <utils/SystemClock.h>
 
 #include <chrono>
+#include <set>
 #include <utility>
 #include <vector>
 
@@ -40,6 +43,7 @@
 
 using AidlSessionHint = aidl::android::hardware::power::SessionHint;
 using AidlSessionMode = aidl::android::hardware::power::SessionMode;
+using android::base::StringPrintf;
 
 struct APerformanceHintSession;
 
@@ -98,10 +102,21 @@
     std::vector<int64_t> mLastHintSentTimestamp;
     // Cached samples
     std::vector<WorkDuration> mActualWorkDurations;
+    std::string mSessionName;
+    static int32_t sIDCounter;
+    // The most recent set of thread IDs
+    std::vector<int32_t> mLastThreadIDs;
+    // Tracing helpers
+    void traceThreads(std::vector<int32_t>& tids);
+    void tracePowerEfficient(bool powerEfficient);
+    void traceActualDuration(int64_t actualDuration);
+    void traceBatchSize(size_t batchSize);
+    void traceTargetDuration(int64_t targetDuration);
 };
 
 static IHintManager* gIHintManagerForTesting = nullptr;
 static APerformanceHintManager* gHintManagerForTesting = nullptr;
+int32_t APerformanceHintSession::sIDCounter = 0;
 
 // ===================================== APerformanceHintManager implementation
 APerformanceHintManager::APerformanceHintManager(sp<IHintManager> manager,
@@ -150,8 +165,12 @@
     if (!ret.isOk() || !session) {
         return nullptr;
     }
-    return new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
-                                       initialTargetWorkDurationNanos);
+    auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
+                                           initialTargetWorkDurationNanos);
+    out->traceThreads(tids);
+    out->traceTargetDuration(initialTargetWorkDurationNanos);
+    out->tracePowerEfficient(false);
+    return out;
 }
 
 int64_t APerformanceHintManager::getPreferredRateNanos() const {
@@ -174,6 +193,7 @@
                                                         ndk::enum_range<AidlSessionHint>().end()};
 
     mLastHintSentTimestamp = std::vector<int64_t>(sessionHintRange.size(), 0);
+    mSessionName = android::base::StringPrintf("ADPF Session %" PRId32, ++sIDCounter);
 }
 
 APerformanceHintSession::~APerformanceHintSession() {
@@ -200,6 +220,8 @@
      * as they are most likely obsolete.
      */
     mActualWorkDurations.clear();
+    traceBatchSize(0);
+    traceTargetDuration(targetDurationNanos);
     mFirstTargetMetTimestamp = 0;
     mLastTargetMetTimestamp = 0;
     return 0;
@@ -254,6 +276,9 @@
         }
         return EPIPE;
     }
+
+    traceThreads(tids);
+
     return 0;
 }
 
@@ -289,6 +314,7 @@
               ret.exceptionMessage().c_str());
         return EPIPE;
     }
+    tracePowerEfficient(enabled);
     return OK;
 }
 
@@ -318,6 +344,7 @@
     int64_t actualTotalDurationNanos = workDuration->actualTotalDurationNanos;
     int64_t now = uptimeNanos();
     workDuration->timestampNanos = now;
+    traceActualDuration(workDuration->actualTotalDurationNanos);
     mActualWorkDurations.push_back(std::move(*workDuration));
 
     if (actualTotalDurationNanos >= mTargetDurationNanos) {
@@ -335,6 +362,7 @@
          */
         if (now - mFirstTargetMetTimestamp > mPreferredRateNanos &&
             now - mLastTargetMetTimestamp <= mPreferredRateNanos) {
+            traceBatchSize(mActualWorkDurations.size());
             return 0;
         }
         mLastTargetMetTimestamp = now;
@@ -346,12 +374,54 @@
               ret.exceptionMessage().c_str());
         mFirstTargetMetTimestamp = 0;
         mLastTargetMetTimestamp = 0;
+        traceBatchSize(mActualWorkDurations.size());
         return ret.exceptionCode() == binder::Status::EX_ILLEGAL_ARGUMENT ? EINVAL : EPIPE;
     }
     mActualWorkDurations.clear();
+    traceBatchSize(0);
 
     return 0;
 }
+// ===================================== Tracing helpers
+
+void APerformanceHintSession::traceThreads(std::vector<int32_t>& tids) {
+    std::set<int32_t> tidSet{tids.begin(), tids.end()};
+
+    // Disable old TID tracing
+    for (int32_t tid : mLastThreadIDs) {
+        if (!tidSet.count(tid)) {
+            std::string traceName =
+                    android::base::StringPrintf("%s TID: %" PRId32, mSessionName.c_str(), tid);
+            ATrace_setCounter(traceName.c_str(), 0);
+        }
+    }
+
+    // Add new TID tracing
+    for (int32_t tid : tids) {
+        std::string traceName =
+                android::base::StringPrintf("%s TID: %" PRId32, mSessionName.c_str(), tid);
+        ATrace_setCounter(traceName.c_str(), 1);
+    }
+
+    mLastThreadIDs = std::move(tids);
+}
+
+void APerformanceHintSession::tracePowerEfficient(bool powerEfficient) {
+    ATrace_setCounter((mSessionName + " power efficiency mode").c_str(), powerEfficient);
+}
+
+void APerformanceHintSession::traceActualDuration(int64_t actualDuration) {
+    ATrace_setCounter((mSessionName + " actual duration").c_str(), actualDuration);
+}
+
+void APerformanceHintSession::traceBatchSize(size_t batchSize) {
+    std::string traceName = StringPrintf("%s batch size", mSessionName.c_str());
+    ATrace_setCounter((mSessionName + " batch size").c_str(), batchSize);
+}
+
+void APerformanceHintSession::traceTargetDuration(int64_t targetDuration) {
+    ATrace_setCounter((mSessionName + " target duration").c_str(), targetDuration);
+}
 
 // ===================================== C API
 APerformanceHintManager* APerformanceHint_getManager() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index d5b5af7..97bbf12 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -81,14 +81,17 @@
 
     @Override
     protected void transferToRoute(@NonNull MediaRoute2Info route) {
-        mRouterManager.transfer(mPackageName, route);
+        // TODO: b/279555229 - provide real user handle of a caller.
+        mRouterManager.transfer(mPackageName, route, android.os.Process.myUserHandle());
     }
 
     @Override
     protected boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device) {
         final RoutingSessionInfo info = mRouterManager.getSystemRoutingSession(null);
         if (info != null) {
-            mRouterManager.transfer(info, device.mRouteInfo);
+            // TODO: b/279555229 - provide real user handle and package name of a caller.
+            mRouterManager.transfer(
+                    info, device.mRouteInfo, android.os.Process.myUserHandle(), mPackageName);
             return true;
         }
         return false;
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index d9fe733..3027c5f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -209,6 +209,7 @@
         VALIDATORS.put(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.POWER_BUTTON_LONG_PRESS_DURATION_MS, NONE_NEGATIVE_LONG_VALIDATOR);
         VALIDATORS.put(Global.STYLUS_EVER_USED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE, BOOLEAN_VALIDATOR);
 
         VALIDATORS.put(Global.Wearable.HAS_PAY_TOKENS, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN, ANY_INTEGER_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 85e8769..6ad10cc 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -350,6 +350,7 @@
                     Settings.Global.DSRM_DURATION_MILLIS,
                     Settings.Global.DSRM_ENABLED_ACTIONS,
                     Settings.Global.MODE_RINGER,
+                    Settings.Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE,
                     Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
                     Settings.Global.MULTI_SIM_SMS_PROMPT,
                     Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index 50ed7ab..7f16ca5 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -23,7 +23,8 @@
     default_visibility: [
         "//visibility:override",
         "//frameworks/base/packages/SystemUI:__subpackages__",
-        "//platform_testing:__subpackages__"
+        "//frameworks/libs/systemui/tracinglib:__subpackages__",
+        "//platform_testing:__subpackages__",
     ],
 }
 
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 98a2d9f..41d12dc 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -205,6 +205,13 @@
 }
 
 flag {
+   name: "pss_task_switcher"
+   namespace: "systemui"
+   description: "Enable the task switcher feature for partial screen sharing"
+   bug: "317208379"
+}
+
+flag {
    name: "rest_to_unlock"
    namespace: "systemui"
    description: "Require prolonged touch for fingerprint authentication"
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 2a9cf0f..55fc3a2 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
@@ -20,10 +20,12 @@
 import android.util.SizeF
 import android.widget.FrameLayout
 import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
@@ -31,11 +33,13 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.grid.GridCells
 import androidx.compose.foundation.lazy.grid.GridItemSpan
 import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
 import androidx.compose.foundation.lazy.grid.rememberLazyGridState
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.Edit
@@ -98,7 +102,6 @@
         modifier = modifier.fillMaxSize().background(Color.White),
     ) {
         CommunalHubLazyGrid(
-            modifier = Modifier.align(Alignment.CenterStart),
             communalContent = communalContent,
             viewModel = viewModel,
             contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize),
@@ -138,21 +141,21 @@
 
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
-private fun CommunalHubLazyGrid(
+private fun BoxScope.CommunalHubLazyGrid(
     communalContent: List<CommunalContentModel>,
     viewModel: BaseCommunalViewModel,
-    modifier: Modifier = Modifier,
     contentPadding: PaddingValues,
     setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit,
     updateDragPositionForRemove: (offset: Offset) -> Boolean,
 ) {
-    var gridModifier = modifier
+    var gridModifier = Modifier.align(Alignment.CenterStart)
     val gridState = rememberLazyGridState()
     var list = communalContent
     var dragDropState: GridDragDropState? = null
     if (viewModel.isEditMode && viewModel is CommunalEditModeViewModel) {
         val contentListState = rememberContentListState(communalContent, viewModel)
         list = contentListState.list
+        // for drag & drop operations within the communal hub grid
         dragDropState =
             rememberGridDragDropState(
                 gridState = gridState,
@@ -164,9 +167,22 @@
                 .fillMaxSize()
                 .dragContainer(dragDropState, beforeContentPadding(contentPadding))
                 .onGloballyPositioned { setGridCoordinates(it) }
+        // for widgets dropped from other activities
+        val dragAndDropTargetState =
+            rememberDragAndDropTargetState(
+                gridState = gridState,
+                contentListState = contentListState,
+                updateDragPositionForRemove = updateDragPositionForRemove
+            )
+
+        // A full size box in background that listens to widget drops from the picker.
+        // Since the grid has its own listener for in-grid drag events, we use a separate element
+        // for android drag events.
+        Box(Modifier.fillMaxSize().dragAndDropTarget(dragAndDropTargetState)) {}
     } else {
         gridModifier = gridModifier.height(Dimensions.GridHeight)
     }
+
     LazyHorizontalGrid(
         modifier = gridModifier,
         state = gridState,
@@ -309,12 +325,24 @@
 ) {
     when (model) {
         is CommunalContentModel.Widget -> WidgetContent(model, size, elevation, modifier)
+        is CommunalContentModel.WidgetPlaceholder -> WidgetPlaceholderContent(size)
         is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier)
         is CommunalContentModel.Tutorial -> TutorialContent(modifier)
         is CommunalContentModel.Umo -> Umo(viewModel, modifier)
     }
 }
 
+/** Presents a placeholder card for the new widget being dragged and dropping into the grid. */
+@Composable
+fun WidgetPlaceholderContent(size: SizeF) {
+    Card(
+        modifier = Modifier.size(Dp(size.width), Dp(size.height)),
+        colors = CardDefaults.cardColors(containerColor = Color.Transparent),
+        border = BorderStroke(3.dp, LocalAndroidColorScheme.current.tertiaryFixed),
+        shape = RoundedCornerShape(16.dp)
+    ) {}
+}
+
 @Composable
 private fun WidgetContent(
     model: CommunalContentModel.Widget,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
index 89c5765..979991d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
@@ -16,11 +16,10 @@
 
 package com.android.systemui.communal.ui.compose
 
+import android.content.ComponentName
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
+import androidx.compose.runtime.toMutableStateList
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
 
@@ -32,6 +31,7 @@
     return remember(communalContent) {
         ContentListState(
             communalContent,
+            viewModel::onAddWidget,
             viewModel::onDeleteWidget,
             viewModel::onReorderWidgets,
         )
@@ -46,30 +46,57 @@
 class ContentListState
 internal constructor(
     communalContent: List<CommunalContentModel>,
+    private val onAddWidget: (componentName: ComponentName, priority: Int) -> Unit,
     private val onDeleteWidget: (id: Int) -> Unit,
-    private val onReorderWidgets: (ids: List<Int>) -> Unit,
+    private val onReorderWidgets: (widgetIdToPriorityMap: Map<Int, Int>) -> Unit,
 ) {
-    var list by mutableStateOf(communalContent)
+    var list = communalContent.toMutableStateList()
         private set
 
     /** Move item to a new position in the list. */
     fun onMove(fromIndex: Int, toIndex: Int) {
-        list = list.toMutableList().apply { add(toIndex, removeAt(fromIndex)) }
+        list.apply { add(toIndex, removeAt(fromIndex)) }
     }
 
     /** Remove widget from the list and the database. */
     fun onRemove(indexToRemove: Int) {
         if (list[indexToRemove] is CommunalContentModel.Widget) {
             val widget = list[indexToRemove] as CommunalContentModel.Widget
-            list = list.toMutableList().apply { removeAt(indexToRemove) }
+            list.apply { removeAt(indexToRemove) }
             onDeleteWidget(widget.appWidgetId)
         }
     }
 
-    /** Persist the new order with all the movements happened during dragging. */
-    fun onSaveList() {
-        val widgetIds: List<Int> =
-            list.filterIsInstance<CommunalContentModel.Widget>().map { it.appWidgetId }
-        onReorderWidgets(widgetIds)
+    /**
+     * Persists the new order with all the movements happened during drag operations & the new
+     * widget drop (if applicable).
+     *
+     * @param newItemComponentName name of the new widget that was dropped into the list; null if no
+     *   new widget was added.
+     * @param newItemIndex index at which the a new widget was dropped into the list; null if no new
+     *   widget was dropped.
+     */
+    fun onSaveList(newItemComponentName: ComponentName? = null, newItemIndex: Int? = null) {
+        // filters placeholder, but, maintains the indices of the widgets as if the placeholder was
+        // in the list. When persisted in DB, this leaves space for the new item (to be added) at
+        // the correct priority.
+        val widgetIdToPriorityMap: Map<Int, Int> =
+            list
+                .mapIndexedNotNull { index, item ->
+                    if (item is CommunalContentModel.Widget) {
+                        item.appWidgetId to list.size - index
+                    } else {
+                        null
+                    }
+                }
+                .toMap()
+        // reorder and then add the new widget
+        onReorderWidgets(widgetIdToPriorityMap)
+        if (newItemComponentName != null && newItemIndex != null) {
+            onAddWidget(newItemComponentName, /*priority=*/ list.size - newItemIndex)
+        }
     }
+
+    /** Returns true if the item at given index is editable. */
+    fun isItemEditable(index: Int) = list[index] is CommunalContentModel.Widget
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
new file mode 100644
index 0000000..22aa837
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
@@ -0,0 +1,274 @@
+/*
+ * 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.communal.ui.compose
+
+import android.content.ClipDescription
+import android.content.ComponentName
+import android.content.Intent
+import android.view.DragEvent
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.draganddrop.dragAndDropTarget
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.scrollBy
+import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
+import androidx.compose.foundation.lazy.grid.LazyGridState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draganddrop.DragAndDropEvent
+import androidx.compose.ui.draganddrop.DragAndDropTarget
+import androidx.compose.ui.draganddrop.mimeTypes
+import androidx.compose.ui.draganddrop.toAndroidDragEvent
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.dp
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.ui.compose.extensions.plus
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+
+/**
+ * Holds state associated with dragging and dropping items from other activities into the lazy grid.
+ *
+ * @see dragAndDropTarget
+ */
+@Composable
+internal fun rememberDragAndDropTargetState(
+    gridState: LazyGridState,
+    contentListState: ContentListState,
+    updateDragPositionForRemove: (offset: Offset) -> Boolean,
+): DragAndDropTargetState {
+    val scope = rememberCoroutineScope()
+    val autoScrollSpeed = remember { mutableFloatStateOf(0f) }
+    // Threshold of distance from edges that should start auto-scroll - chosen to be a narrow value
+    // that allows differentiating intention of scrolling from intention of dragging over the first
+    // visible item.
+    val autoScrollThreshold = with(LocalDensity.current) { 60.dp.toPx() }
+    val state =
+        remember(gridState, contentListState) {
+            DragAndDropTargetState(
+                state = gridState,
+                contentListState = contentListState,
+                scope = scope,
+                autoScrollSpeed = autoScrollSpeed,
+                autoScrollThreshold = autoScrollThreshold,
+                updateDragPositionForRemove = updateDragPositionForRemove,
+            )
+        }
+    LaunchedEffect(autoScrollSpeed.floatValue) {
+        if (autoScrollSpeed.floatValue != 0f) {
+            while (isActive) {
+                gridState.scrollBy(autoScrollSpeed.floatValue)
+                delay(10)
+            }
+        }
+    }
+    return state
+}
+
+/**
+ * Attaches a listener for drag and drop events from other activities.
+ *
+ * @see androidx.compose.foundation.draganddrop.dragAndDropTarget
+ * @see DragEvent
+ */
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+internal fun Modifier.dragAndDropTarget(
+    dragDropTargetState: DragAndDropTargetState,
+): Modifier {
+    val state by rememberUpdatedState(dragDropTargetState)
+
+    return this then
+        Modifier.dragAndDropTarget(
+            shouldStartDragAndDrop = accept@{ startEvent ->
+                    startEvent.mimeTypes().any { it == ClipDescription.MIMETYPE_TEXT_INTENT }
+                },
+            target =
+                object : DragAndDropTarget {
+                    override fun onStarted(event: DragAndDropEvent) {
+                        state.onStarted()
+                    }
+
+                    override fun onMoved(event: DragAndDropEvent) {
+                        state.onMoved(event)
+                    }
+
+                    override fun onDrop(event: DragAndDropEvent): Boolean {
+                        return state.onDrop(event)
+                    }
+
+                    override fun onEnded(event: DragAndDropEvent) {
+                        state.onEnded()
+                    }
+                }
+        )
+}
+
+/**
+ * Handles dropping of an item coming from a different activity (e.g. widget picker) in to the grid
+ * corresponding to the provided [LazyGridState].
+ *
+ * Adds a placeholder container to highlight the anticipated location the widget will be dropped to.
+ * When the item is held over an empty area, the placeholder appears at the end of the grid if one
+ * didn't exist already. As user moves the item over an existing item, the placeholder appears in
+ * place of that existing item. And then, the existing item is pushed over as part of re-ordering.
+ *
+ * Once item is dropped, new ordering along with the dropped item is persisted. See
+ * [ContentListState.onSaveList].
+ *
+ * Difference between this and [GridDragDropState] is that, this is used for listening to drops from
+ * other activities. [GridDragDropState] on the other hand, handles dragging of existing items in
+ * the communal hub grid.
+ */
+internal class DragAndDropTargetState(
+    private val state: LazyGridState,
+    private val contentListState: ContentListState,
+    private val scope: CoroutineScope,
+    private val autoScrollSpeed: MutableState<Float>,
+    private val autoScrollThreshold: Float,
+    private val updateDragPositionForRemove: (offset: Offset) -> Boolean,
+) {
+    /**
+     * The placeholder item that is treated as if it is being dragged across the grid. It is added
+     * to grid once drag and drop event is started and removed when event ends.
+     */
+    private var placeHolder = CommunalContentModel.WidgetPlaceholder()
+
+    private var placeHolderIndex: Int? = null
+    private var isOnRemoveButton = false
+
+    fun onStarted() {
+        // assume item will be added to the end.
+        contentListState.list.add(placeHolder)
+        placeHolderIndex = contentListState.list.size - 1
+    }
+
+    fun onMoved(event: DragAndDropEvent) {
+        val dragEvent = event.toAndroidDragEvent()
+        isOnRemoveButton = updateDragPositionForRemove(Offset(dragEvent.x, dragEvent.y))
+        if (!isOnRemoveButton) {
+            findTargetItem(dragEvent)?.apply {
+                var scrollIndex: Int? = null
+                var scrollOffset: Int? = null
+                if (placeHolderIndex == state.firstVisibleItemIndex) {
+                    // Save info about the first item before the move, to neutralize the automatic
+                    // keeping first item first.
+                    scrollIndex = placeHolderIndex
+                    scrollOffset = state.firstVisibleItemScrollOffset
+                }
+
+                autoScrollIfNearEdges(dragEvent)
+
+                if (contentListState.isItemEditable(this.index)) {
+                    movePlaceholderTo(this.index)
+                    placeHolderIndex = this.index
+                }
+
+                if (scrollIndex != null && scrollOffset != null) {
+                    // this is needed to neutralize automatic keeping the first item first.
+                    scope.launch { state.scrollToItem(scrollIndex, scrollOffset) }
+                }
+            }
+        }
+    }
+
+    fun onDrop(event: DragAndDropEvent): Boolean {
+        autoScrollSpeed.value = 0f
+        if (isOnRemoveButton) {
+            return false
+        }
+        return placeHolderIndex?.let { dropIndex ->
+            val componentName = event.maybeWidgetComponentName()
+            if (componentName != null) {
+                // Placeholder isn't removed yet to allow the setting the right priority for items
+                // before adding in the new item.
+                contentListState.onSaveList(
+                    newItemComponentName = componentName,
+                    newItemIndex = dropIndex
+                )
+                return@let true
+            }
+            return false
+        }
+            ?: false
+    }
+
+    fun onEnded() {
+        autoScrollSpeed.value = 0f
+        placeHolderIndex = null
+        contentListState.list.remove(placeHolder)
+        isOnRemoveButton = updateDragPositionForRemove(Offset.Zero)
+    }
+
+    private fun autoScrollIfNearEdges(dragEvent: DragEvent) {
+        val orientation = state.layoutInfo.orientation
+        val distanceFromStart =
+            if (orientation == Orientation.Horizontal) {
+                dragEvent.x
+            } else {
+                dragEvent.y
+            }
+        val distanceFromEnd =
+            if (orientation == Orientation.Horizontal) {
+                state.layoutInfo.viewportSize.width - dragEvent.x
+            } else {
+                state.layoutInfo.viewportSize.height - dragEvent.y
+            }
+        autoScrollSpeed.value =
+            when {
+                distanceFromEnd < autoScrollThreshold -> autoScrollThreshold - distanceFromEnd
+                distanceFromStart < autoScrollThreshold ->
+                    -(autoScrollThreshold - distanceFromStart)
+                else -> 0f
+            }
+    }
+
+    private fun findTargetItem(dragEvent: DragEvent): LazyGridItemInfo? =
+        state.layoutInfo.visibleItemsInfo.firstOrNull { item ->
+            dragEvent.x.toInt() in item.offset.x..(item.offset + item.size).x &&
+                dragEvent.y.toInt() in item.offset.y..(item.offset + item.size).y
+        }
+
+    private fun movePlaceholderTo(index: Int) {
+        val currentIndex = contentListState.list.indexOf(placeHolder)
+        if (currentIndex != index) {
+            contentListState.onMove(currentIndex, index)
+        }
+    }
+
+    /**
+     * Parses and returns the component name of the widget that was dropped into the communal grid.
+     *
+     * Returns null if the drop event didn't include the widget information.
+     */
+    private fun DragAndDropEvent.maybeWidgetComponentName(): ComponentName? {
+        val clipData = this.toAndroidDragEvent().clipData.takeIf { it.itemCount != 0 }
+        return clipData
+            ?.getItemAt(0)
+            ?.intent
+            ?.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName::class.java)
+    }
+}
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 5451d05..0d460aa8 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
@@ -32,15 +32,13 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.unit.IntOffset
-import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.toOffset
 import androidx.compose.ui.unit.toSize
 import androidx.compose.ui.zIndex
-import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.ui.compose.extensions.plus
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.launch
@@ -112,7 +110,7 @@
             .firstOrNull { item ->
                 // grid item offset is based off grid content container so we need to deduct
                 // before content padding from the initial pointer position
-                item.isEditable &&
+                contentListState.isItemEditable(item.index) &&
                     (offset.x - contentOffset.x).toInt() in item.offset.x..item.offsetEnd.x &&
                     (offset.y - contentOffset.y).toInt() in item.offset.y..item.offsetEnd.y
             }
@@ -149,7 +147,7 @@
 
         val targetItem =
             state.layoutInfo.visibleItemsInfo.find { item ->
-                item.isEditable &&
+                contentListState.isItemEditable(item.index) &&
                     middleOffset.x.toInt() in item.offset.x..item.offsetEnd.x &&
                     middleOffset.y.toInt() in item.offset.y..item.offsetEnd.y &&
                     draggingItem.index != item.index
@@ -187,10 +185,6 @@
     private val LazyGridItemInfo.offsetEnd: IntOffset
         get() = this.offset + this.size
 
-    /** Whether the grid item can be dragged or be a drop target. Only widget card is editable. */
-    private val LazyGridItemInfo.isEditable: Boolean
-        get() = contentListState.list[this.index] is CommunalContentModel.Widget
-
     /** Calculate the amount dragged out of bound on both sides. Returns 0f if not overscrolled */
     private fun checkForOverscroll(startOffset: Offset, endOffset: Offset): Float {
         return when {
@@ -210,14 +204,6 @@
     }
 }
 
-private operator fun IntOffset.plus(size: IntSize): IntOffset {
-    return IntOffset(x + size.width, y + size.height)
-}
-
-private operator fun Offset.plus(size: Size): Offset {
-    return Offset(x + size.width, y + size.height)
-}
-
 fun Modifier.dragContainer(
     dragDropState: GridDragDropState,
     beforeContentPadding: ContentPaddingInPx
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/extensions/OffsetOperators.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/extensions/OffsetOperators.kt
new file mode 100644
index 0000000..b86c07e
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/extensions/OffsetOperators.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.compose.extensions
+
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+
+/** Adds the given size to the x and y offsets in this [IntOffset] */
+operator fun IntOffset.plus(size: IntSize): IntOffset {
+    return IntOffset(x + size.width, y + size.height)
+}
+
+/** Adds the given size to the x and y offsets in this [Offset]. */
+operator fun Offset.plus(size: Size): Offset {
+    return Offset(x + size.width, y + size.height)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BlueprintAlignmentLines.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BlueprintAlignmentLines.kt
new file mode 100644
index 0000000..efa8cc7
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BlueprintAlignmentLines.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.composable.blueprint
+
+import androidx.compose.ui.layout.HorizontalAlignmentLine
+import androidx.compose.ui.layout.VerticalAlignmentLine
+import kotlin.math.max
+import kotlin.math.min
+
+/**
+ * Encapsulates all blueprint alignment lines.
+ *
+ * These can be used to communicate alignment lines emitted by elements that the blueprint should
+ * consume and use to know how to constrain and/or place other elements in that blueprint.
+ *
+ * For more information, please see
+ * [the official documentation](https://developer.android.com/jetpack/compose/layouts/alignment-lines).
+ */
+object BlueprintAlignmentLines {
+
+    /**
+     * Encapsulates alignment lines produced by the lock icon element.
+     *
+     * Because the lock icon is also the same element as the under-display fingerprint sensor
+     * (UDFPS), blueprints should use its alignment lines to make sure that other elements on screen
+     * do not overlap with the lock icon.
+     */
+    object LockIcon {
+
+        /** The left edge of the lock icon. */
+        val Left =
+            VerticalAlignmentLine(
+                merger = { old, new ->
+                    // When two left alignment line values are provided, choose the leftmost one:
+                    min(old, new)
+                },
+            )
+
+        /** The top edge of the lock icon. */
+        val Top =
+            HorizontalAlignmentLine(
+                merger = { old, new ->
+                    // When two top alignment line values are provided, choose the topmost one:
+                    min(old, new)
+                },
+            )
+
+        /** The right edge of the lock icon. */
+        val Right =
+            VerticalAlignmentLine(
+                merger = { old, new ->
+                    // When two right alignment line values are provided, choose the rightmost one:
+                    max(old, new)
+                },
+            )
+
+        /** The bottom edge of the lock icon. */
+        val Bottom =
+            HorizontalAlignmentLine(
+                merger = { old, new ->
+                    // When two bottom alignment line values are provided, choose the bottommost
+                    // one:
+                    max(old, new)
+                },
+            )
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index fc1df84..7314453 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -16,18 +16,13 @@
 
 package com.android.systemui.keyguard.ui.composable.blueprint
 
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.offset
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.unit.IntRect
 import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.height
-import com.android.compose.modifiers.width
 import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
 import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
 import com.android.systemui.keyguard.ui.composable.section.ClockSection
@@ -62,47 +57,104 @@
 
     @Composable
     override fun SceneScope.Content(modifier: Modifier) {
-        val context = LocalContext.current
-        val lockIconBounds = lockSection.lockIconBounds(context)
         val isUdfpsVisible = viewModel.isUdfpsVisible
 
-        Box(
+        Layout(
+            content = {
+                // Constrained to above the lock icon.
+                Column(
+                    modifier = Modifier.fillMaxWidth(),
+                ) {
+                    with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+                    with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
+                    with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
+                    with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+                    with(notificationSection) {
+                        Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
+                    }
+                    if (!isUdfpsVisible) {
+                        with(ambientIndicationSection) {
+                            AmbientIndication(modifier = Modifier.fillMaxWidth())
+                        }
+                    }
+                }
+
+                with(lockSection) { LockIcon() }
+
+                // Aligned to bottom and constrained to below the lock icon.
+                Column(modifier = Modifier.fillMaxWidth()) {
+                    if (isUdfpsVisible) {
+                        with(ambientIndicationSection) {
+                            AmbientIndication(modifier = Modifier.fillMaxWidth())
+                        }
+                    }
+
+                    with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) }
+                }
+
+                // Aligned to bottom and NOT constrained by the lock icon.
+                with(bottomAreaSection) {
+                    Shortcut(isStart = true, applyPadding = true)
+                    Shortcut(isStart = false, applyPadding = true)
+                }
+            },
             modifier = modifier,
-        ) {
-            Column(
-                modifier = Modifier.fillMaxWidth().height { lockIconBounds.top },
-            ) {
-                with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
-                with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
-                with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
-                with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
-                with(notificationSection) {
-                    Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
-                }
-                if (!isUdfpsVisible) {
-                    with(ambientIndicationSection) {
-                        AmbientIndication(modifier = Modifier.fillMaxWidth())
-                    }
-                }
-            }
+        ) { measurables, constraints ->
+            check(measurables.size == 5)
+            val (
+                aboveLockIconMeasurable,
+                lockIconMeasurable,
+                belowLockIconMeasurable,
+                startShortcutMeasurable,
+                endShortcutMeasurable,
+            ) = measurables
 
-            with(lockSection) {
-                LockIcon(
-                    modifier =
-                        Modifier.width { lockIconBounds.width() }
-                            .height { lockIconBounds.height() }
-                            .offset { IntOffset(lockIconBounds.left, lockIconBounds.top) }
+            val noMinConstraints =
+                constraints.copy(
+                    minWidth = 0,
+                    minHeight = 0,
                 )
-            }
+            val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+            val lockIconBounds =
+                IntRect(
+                    left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+                    top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+                    right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+                    bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+                )
 
-            Column(modifier = Modifier.fillMaxWidth().align(Alignment.BottomCenter)) {
-                if (isUdfpsVisible) {
-                    with(ambientIndicationSection) {
-                        AmbientIndication(modifier = Modifier.fillMaxWidth())
-                    }
-                }
+            val aboveLockIconPlaceable =
+                aboveLockIconMeasurable.measure(
+                    noMinConstraints.copy(maxHeight = lockIconBounds.top)
+                )
+            val belowLockIconPlaceable =
+                belowLockIconMeasurable.measure(
+                    noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom)
+                )
+            val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
+            val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
 
-                with(bottomAreaSection) { BottomArea(modifier = Modifier.fillMaxWidth()) }
+            layout(constraints.maxWidth, constraints.maxHeight) {
+                aboveLockIconPlaceable.place(
+                    x = 0,
+                    y = 0,
+                )
+                lockIconPlaceable.place(
+                    x = lockIconBounds.left,
+                    y = lockIconBounds.top,
+                )
+                belowLockIconPlaceable.place(
+                    x = 0,
+                    y = constraints.maxHeight - belowLockIconPlaceable.height,
+                )
+                startShortcutPleaceable.place(
+                    x = 0,
+                    y = constraints.maxHeight - startShortcutPleaceable.height,
+                )
+                endShortcutPleaceable.place(
+                    x = constraints.maxWidth - endShortcutPleaceable.width,
+                    y = constraints.maxHeight - endShortcutPleaceable.height,
+                )
             }
         }
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index fa913f1..4c119c7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -16,24 +16,13 @@
 
 package com.android.systemui.keyguard.ui.composable.blueprint
 
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.offset
-import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.unit.IntRect
 import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.height
-import com.android.compose.modifiers.width
 import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
 import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
 import com.android.systemui.keyguard.ui.composable.section.ClockSection
@@ -42,12 +31,10 @@
 import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
 import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
 import javax.inject.Inject
-import kotlin.math.roundToInt
 
 /**
  * Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form
@@ -70,96 +57,107 @@
 
     @Composable
     override fun SceneScope.Content(modifier: Modifier) {
-        val context = LocalContext.current
-        val lockIconBounds = lockSection.lockIconBounds(context)
         val isUdfpsVisible = viewModel.isUdfpsVisible
 
-        Box(
+        Layout(
+            content = {
+                // Constrained to above the lock icon.
+                Column(
+                    modifier = Modifier.fillMaxWidth(),
+                ) {
+                    with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+                    with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
+                    with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
+                    with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+                    with(notificationSection) {
+                        Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
+                    }
+                    if (!isUdfpsVisible) {
+                        with(ambientIndicationSection) {
+                            AmbientIndication(modifier = Modifier.fillMaxWidth())
+                        }
+                    }
+                }
+
+                // Constrained to the left of the lock icon (in left-to-right layouts).
+                with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) }
+
+                with(lockSection) { LockIcon() }
+
+                // Constrained to the right of the lock icon (in left-to-right layouts).
+                with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) }
+
+                // Aligned to bottom and constrained to below the lock icon.
+                Column(modifier = Modifier.fillMaxWidth()) {
+                    if (isUdfpsVisible) {
+                        with(ambientIndicationSection) {
+                            AmbientIndication(modifier = Modifier.fillMaxWidth())
+                        }
+                    }
+
+                    with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) }
+                }
+            },
             modifier = modifier,
-        ) {
-            Column(
-                modifier = Modifier.fillMaxWidth().height { lockIconBounds.top },
-            ) {
-                with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
-                with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
-                with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
-                with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
-                with(notificationSection) {
-                    Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
-                }
-                if (!isUdfpsVisible) {
-                    with(ambientIndicationSection) {
-                        AmbientIndication(modifier = Modifier.fillMaxWidth())
-                    }
-                }
-            }
+        ) { measurables, constraints ->
+            check(measurables.size == 5)
+            val (
+                aboveLockIconMeasurable,
+                startSideShortcutMeasurable,
+                lockIconMeasurable,
+                endSideShortcutMeasurable,
+                belowLockIconMeasurable,
+            ) = measurables
 
-            val shortcutSizePx =
-                with(LocalDensity.current) { bottomAreaSection.shortcutSizeDp().toSize() }
+            val noMinConstraints =
+                constraints.copy(
+                    minWidth = 0,
+                    minHeight = 0,
+                )
 
-            Row(
-                verticalAlignment = Alignment.CenterVertically,
-                modifier =
-                    Modifier.fillMaxWidth().offset {
-                        val rowTop =
-                            if (shortcutSizePx.height > lockIconBounds.height()) {
-                                (lockIconBounds.top -
-                                        (shortcutSizePx.height + lockIconBounds.height()) / 2)
-                                    .roundToInt()
-                            } else {
-                                lockIconBounds.top
-                            }
+            val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+            val lockIconBounds =
+                IntRect(
+                    left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+                    top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+                    right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+                    bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+                )
 
-                        IntOffset(0, rowTop)
-                    },
-            ) {
-                Spacer(Modifier.weight(1f))
+            val aboveLockIconPlaceable =
+                aboveLockIconMeasurable.measure(
+                    noMinConstraints.copy(maxHeight = lockIconBounds.top)
+                )
+            val startSideShortcutPlaceable = startSideShortcutMeasurable.measure(noMinConstraints)
+            val endSideShortcutPlaceable = endSideShortcutMeasurable.measure(noMinConstraints)
+            val belowLockIconPlaceable =
+                belowLockIconMeasurable.measure(
+                    noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom)
+                )
 
-                with(bottomAreaSection) { Shortcut(isStart = true) }
-
-                Spacer(Modifier.weight(1f))
-
-                with(lockSection) {
-                    LockIcon(
-                        modifier =
-                            Modifier.width { lockIconBounds.width() }
-                                .height { lockIconBounds.height() }
-                    )
-                }
-
-                Spacer(Modifier.weight(1f))
-
-                with(bottomAreaSection) { Shortcut(isStart = false) }
-
-                Spacer(Modifier.weight(1f))
-            }
-
-            Column(modifier = Modifier.fillMaxWidth().align(Alignment.BottomCenter)) {
-                if (isUdfpsVisible) {
-                    with(ambientIndicationSection) {
-                        AmbientIndication(modifier = Modifier.fillMaxWidth())
-                    }
-                }
-
-                with(bottomAreaSection) {
-                    IndicationArea(
-                        modifier =
-                            Modifier.fillMaxWidth()
-                                .padding(
-                                    horizontal =
-                                        dimensionResource(
-                                            R.dimen.keyguard_affordance_horizontal_offset
-                                        )
-                                )
-                                .padding(
-                                    bottom =
-                                        dimensionResource(
-                                            R.dimen.keyguard_affordance_vertical_offset
-                                        )
-                                )
-                                .heightIn(min = shortcutSizeDp().height),
-                    )
-                }
+            layout(constraints.maxWidth, constraints.maxHeight) {
+                aboveLockIconPlaceable.place(
+                    x = 0,
+                    y = 0,
+                )
+                startSideShortcutPlaceable.placeRelative(
+                    x = lockIconBounds.left / 2 - startSideShortcutPlaceable.width / 2,
+                    y = lockIconBounds.center.y - startSideShortcutPlaceable.height / 2,
+                )
+                lockIconPlaceable.place(
+                    x = lockIconBounds.left,
+                    y = lockIconBounds.top,
+                )
+                endSideShortcutPlaceable.placeRelative(
+                    x =
+                        lockIconBounds.right + (constraints.maxWidth - lockIconBounds.right) / 2 -
+                            endSideShortcutPlaceable.width / 2,
+                    y = lockIconBounds.center.y - endSideShortcutPlaceable.height / 2,
+                )
+                belowLockIconPlaceable.place(
+                    x = 0,
+                    y = constraints.maxHeight - belowLockIconPlaceable.height,
+                )
             }
         }
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 53e4be3..db20f65 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -19,7 +19,6 @@
 import android.view.View
 import android.widget.ImageView
 import androidx.annotation.IdRes
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
@@ -58,38 +57,17 @@
     private val indicationAreaViewModel: KeyguardIndicationAreaViewModel,
     private val keyguardRootViewModel: KeyguardRootViewModel,
 ) {
-    @Composable
-    fun SceneScope.BottomArea(
-        modifier: Modifier = Modifier,
-    ) {
-        Row(
-            modifier =
-                modifier
-                    .padding(
-                        horizontal =
-                            dimensionResource(R.dimen.keyguard_affordance_horizontal_offset)
-                    )
-                    .padding(
-                        bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset)
-                    ),
-        ) {
-            Shortcut(
-                isStart = true,
-            )
-
-            IndicationArea(
-                modifier = Modifier.weight(1f),
-            )
-
-            Shortcut(
-                isStart = false,
-            )
-        }
-    }
-
+    /**
+     * Renders a single lockscreen shortcut.
+     *
+     * @param isStart Whether the shortcut goes on the left (in left-to-right locales).
+     * @param applyPadding Whether to apply padding around the shortcut, this is needed if the
+     *   shortcut is placed along the edges of the display.
+     */
     @Composable
     fun SceneScope.Shortcut(
         isStart: Boolean,
+        applyPadding: Boolean,
         modifier: Modifier = Modifier,
     ) {
         MovableElement(
@@ -103,6 +81,12 @@
                 falsingManager = falsingManager,
                 vibratorHelper = vibratorHelper,
                 indicationController = indicationController,
+                modifier =
+                    if (applyPadding) {
+                        Modifier.shortcutPadding()
+                    } else {
+                        Modifier
+                    }
             )
         }
     }
@@ -113,7 +97,7 @@
     ) {
         MovableElement(
             key = IndicationAreaElementKey,
-            modifier = modifier,
+            modifier = modifier.shortcutPadding(),
         ) {
             IndicationArea(
                 indicationAreaViewModel = indicationAreaViewModel,
@@ -218,6 +202,14 @@
             modifier = modifier.fillMaxWidth(),
         )
     }
+
+    @Composable
+    private fun Modifier.shortcutPadding(): Modifier {
+        return this.padding(
+                horizontal = dimensionResource(R.dimen.keyguard_affordance_horizontal_offset)
+            )
+            .padding(bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset))
+    }
 }
 
 private val StartButtonElementKey = ElementKey("StartButton")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index 8bbe424b..d93863d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -17,8 +17,6 @@
 package com.android.systemui.keyguard.ui.composable.section
 
 import android.content.Context
-import android.graphics.Point
-import android.graphics.Rect
 import android.util.DisplayMetrics
 import android.view.WindowManager
 import androidx.compose.foundation.background
@@ -28,11 +26,17 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
 import com.android.systemui.res.R
 import javax.inject.Inject
 
@@ -49,8 +53,33 @@
             key = LockIconElementKey,
             modifier = modifier,
         ) {
+            val context = LocalContext.current
             Box(
-                modifier = Modifier.background(Color.Red),
+                modifier =
+                    Modifier.background(Color.Red).layout { measurable, _ ->
+                        val lockIconBounds = lockIconBounds(context)
+                        val placeable =
+                            measurable.measure(
+                                Constraints.fixed(
+                                    width = lockIconBounds.width,
+                                    height = lockIconBounds.height,
+                                )
+                            )
+                        layout(
+                            width = placeable.width,
+                            height = placeable.height,
+                            alignmentLines =
+                                mapOf(
+                                    BlueprintAlignmentLines.LockIcon.Left to lockIconBounds.left,
+                                    BlueprintAlignmentLines.LockIcon.Top to lockIconBounds.top,
+                                    BlueprintAlignmentLines.LockIcon.Right to lockIconBounds.right,
+                                    BlueprintAlignmentLines.LockIcon.Bottom to
+                                        lockIconBounds.bottom,
+                                ),
+                        ) {
+                            placeable.place(0, 0)
+                        }
+                    },
             ) {
                 Text(
                     text = "TODO(b/316211368): Lock",
@@ -67,9 +96,9 @@
      * On devices that support UDFPS (under-display fingerprint sensor), the bounds of the icon are
      * the same as the bounds of the sensor.
      */
-    fun lockIconBounds(
+    private fun lockIconBounds(
         context: Context,
-    ): Rect {
+    ): IntRect {
         val windowViewBounds = windowManager.currentWindowMetrics.bounds
         var widthPx = windowViewBounds.right.toFloat()
         if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
@@ -85,36 +114,33 @@
         val lockIconRadiusPx = (defaultDensity * 36).toInt()
 
         val udfpsLocation = authController.udfpsLocation
-        return if (authController.isUdfpsSupported && udfpsLocation != null) {
-            centerLockIcon(udfpsLocation, authController.udfpsRadius)
-        } else {
-            val scaleFactor = authController.scaleFactor
-            val bottomPaddingPx =
-                context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
-            val heightPx = windowViewBounds.bottom.toFloat()
+        val (center, radius) =
+            if (authController.isUdfpsSupported && udfpsLocation != null) {
+                Pair(
+                    IntOffset(
+                        x = udfpsLocation.x,
+                        y = udfpsLocation.y,
+                    ),
+                    authController.udfpsRadius.toInt(),
+                )
+            } else {
+                val scaleFactor = authController.scaleFactor
+                val bottomPaddingPx =
+                    context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
+                val heightPx = windowViewBounds.bottom.toFloat()
 
-            centerLockIcon(
-                Point(
-                    (widthPx / 2).toInt(),
-                    (heightPx - ((bottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt()
-                ),
-                lockIconRadiusPx * scaleFactor
-            )
-        }
-    }
+                Pair(
+                    IntOffset(
+                        x = (widthPx / 2).toInt(),
+                        y =
+                            (heightPx - ((bottomPaddingPx + lockIconRadiusPx) * scaleFactor))
+                                .toInt(),
+                    ),
+                    (lockIconRadiusPx * scaleFactor).toInt(),
+                )
+            }
 
-    private fun centerLockIcon(
-        center: Point,
-        radius: Float,
-    ): Rect {
-        return Rect().apply {
-            set(
-                center.x - radius.toInt(),
-                center.y - radius.toInt(),
-                center.x + radius.toInt(),
-                center.y + radius.toInt(),
-            )
-        }
+        return IntRect(center, radius)
     }
 }
 
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index b9d6643..2bfa7d9 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -33,6 +33,8 @@
 import com.android.systemui.animation.GlyphCallback
 import com.android.systemui.animation.TextAnimator
 import com.android.systemui.customization.R
+import com.android.systemui.log.core.LogcatOnlyMessageBuffer
+import com.android.systemui.log.core.LogLevel
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.core.MessageBuffer
 import java.io.PrintWriter
@@ -51,12 +53,13 @@
     defStyleAttr: Int = 0,
     defStyleRes: Int = 0
 ) : TextView(context, attrs, defStyleAttr, defStyleRes) {
-    var messageBuffer: MessageBuffer? = null
-        set(value) {
-            logger = if (value != null) Logger(value, TAG) else null
-        }
-
-    private var logger: Logger? = null
+    // To protect us from issues from this being null while the TextView constructor is running, we
+    // implement the get method and ensure a value is returned before initialization is complete.
+    private var logger = DEFAULT_LOGGER
+        get() = field ?: DEFAULT_LOGGER
+    var messageBuffer: MessageBuffer
+        get() = logger.buffer
+        set(value) { logger = Logger(value, TAG) }
 
     private val time = Calendar.getInstance()
 
@@ -133,8 +136,8 @@
     }
 
     override fun onAttachedToWindow() {
+        logger.d("onAttachedToWindow")
         super.onAttachedToWindow()
-        logger?.d("onAttachedToWindow")
         refreshFormat()
     }
 
@@ -150,13 +153,13 @@
         time.timeInMillis = timeOverrideInMillis ?: System.currentTimeMillis()
         contentDescription = DateFormat.format(descFormat, time)
         val formattedText = DateFormat.format(format, time)
-        logger?.d({ "refreshTime: new formattedText=$str1" }) { str1 = formattedText?.toString() }
+        logger.d({ "refreshTime: new formattedText=$str1" }) { str1 = formattedText?.toString() }
         // Setting text actually triggers a layout pass (because the text view is set to
         // wrap_content width and TextView always relayouts for this). Avoid needless
         // relayout if the text didn't actually change.
         if (!TextUtils.equals(text, formattedText)) {
             text = formattedText
-            logger?.d({ "refreshTime: done setting new time text to: $str1" }) {
+            logger.d({ "refreshTime: done setting new time text to: $str1" }) {
                 str1 = formattedText?.toString()
             }
             // Because the TextLayout may mutate under the hood as a result of the new text, we
@@ -165,21 +168,22 @@
             // without being notified TextInterpolator being notified.
             if (layout != null) {
                 textAnimator?.updateLayout(layout)
-                logger?.d("refreshTime: done updating textAnimator layout")
+                logger.d("refreshTime: done updating textAnimator layout")
             }
             requestLayout()
-            logger?.d("refreshTime: after requestLayout")
+            logger.d("refreshTime: after requestLayout")
         }
     }
 
     fun onTimeZoneChanged(timeZone: TimeZone?) {
+        logger.d({ "onTimeZoneChanged($str1)" }) { str1 = timeZone?.toString() }
         time.timeZone = timeZone
         refreshFormat()
-        logger?.d({ "onTimeZoneChanged newTimeZone=$str1" }) { str1 = timeZone?.toString() }
     }
 
     @SuppressLint("DrawAllocation")
     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        logger.d("onMeasure")
         super.onMeasure(widthMeasureSpec, heightMeasureSpec)
         val animator = textAnimator
         if (animator == null) {
@@ -189,10 +193,10 @@
         } else {
             animator.updateLayout(layout)
         }
-        logger?.d("onMeasure")
     }
 
     override fun onDraw(canvas: Canvas) {
+        logger.d({ "onDraw($str1)"}) { str1 = text.toString() }
         // Use textAnimator to render text if animation is enabled.
         // Otherwise default to using standard draw functions.
         if (isAnimationEnabled) {
@@ -201,22 +205,23 @@
         } else {
             super.onDraw(canvas)
         }
-        logger?.d("onDraw")
     }
 
     override fun invalidate() {
+        @Suppress("UNNECESSARY_SAFE_CALL")
+        // logger won't be initialized when called by TextView's constructor
+        logger.d("invalidate")
         super.invalidate()
-        logger?.d("invalidate")
     }
 
     override fun onTextChanged(
-            text: CharSequence,
-            start: Int,
-            lengthBefore: Int,
-            lengthAfter: Int
+        text: CharSequence,
+        start: Int,
+        lengthBefore: Int,
+        lengthAfter: Int
     ) {
+        logger.d({ "onTextChanged($str1)" }) { str1 = text.toString() }
         super.onTextChanged(text, start, lengthBefore, lengthAfter)
-        logger?.d({ "onTextChanged text=$str1" }) { str1 = text.toString() }
     }
 
     fun setLineSpacingScale(scale: Float) {
@@ -230,7 +235,7 @@
     }
 
     fun animateColorChange() {
-        logger?.d("animateColorChange")
+        logger.d("animateColorChange")
         setTextStyle(
             weight = lockScreenWeight,
             textSize = -1f,
@@ -252,7 +257,7 @@
     }
 
     fun animateAppearOnLockscreen() {
-        logger?.d("animateAppearOnLockscreen")
+        logger.d("animateAppearOnLockscreen")
         setTextStyle(
             weight = dozingWeight,
             textSize = -1f,
@@ -278,7 +283,7 @@
         if (isAnimationEnabled && textAnimator == null) {
             return
         }
-        logger?.d("animateFoldAppear")
+        logger.d("animateFoldAppear")
         setTextStyle(
             weight = lockScreenWeightInternal,
             textSize = -1f,
@@ -305,7 +310,7 @@
             // Skip charge animation if dozing animation is already playing.
             return
         }
-        logger?.d("animateCharge")
+        logger.d("animateCharge")
         val startAnimPhase2 = Runnable {
             setTextStyle(
                 weight = if (isDozing()) dozingWeight else lockScreenWeight,
@@ -329,7 +334,7 @@
     }
 
     fun animateDoze(isDozing: Boolean, animate: Boolean) {
-        logger?.d("animateDoze")
+        logger.d("animateDoze")
         setTextStyle(
             weight = if (isDozing) dozingWeight else lockScreenWeight,
             textSize = -1f,
@@ -448,7 +453,7 @@
             isSingleLineInternal && !use24HourFormat -> Patterns.sClockView12
             else -> DOUBLE_LINE_FORMAT_12_HOUR
         }
-        logger?.d({ "refreshFormat format=$str1" }) { str1 = format?.toString() }
+        logger.d({ "refreshFormat($str1)" }) { str1 = format?.toString() }
 
         descFormat = if (use24HourFormat) Patterns.sClockView24 else Patterns.sClockView12
         refreshTime()
@@ -552,6 +557,8 @@
 
     companion object {
         private val TAG = AnimatableClockView::class.simpleName!!
+        private val DEFAULT_LOGGER = Logger(LogcatOnlyMessageBuffer(LogLevel.WARNING), TAG)
+
         const val ANIMATION_DURATION_FOLD_TO_AOD: Int = 600
         private const val DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm"
         private const val DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm"
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index cdd074d..41bde52 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -21,20 +21,16 @@
 import android.net.Uri
 import android.os.UserHandle
 import android.provider.Settings
-import android.util.Log
 import androidx.annotation.OpenForTesting
-import com.android.systemui.log.LogMessageImpl
 import com.android.systemui.log.core.LogLevel
-import com.android.systemui.log.core.LogMessage
+import com.android.systemui.log.core.LogcatOnlyMessageBuffer
 import com.android.systemui.log.core.Logger
-import com.android.systemui.log.core.MessageBuffer
-import com.android.systemui.log.core.MessageInitializer
-import com.android.systemui.log.core.MessagePrinter
 import com.android.systemui.plugins.PluginLifecycleManager
 import com.android.systemui.plugins.PluginListener
 import com.android.systemui.plugins.PluginManager
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockId
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockMetadata
 import com.android.systemui.plugins.clocks.ClockProvider
 import com.android.systemui.plugins.clocks.ClockProviderPlugin
@@ -77,32 +73,6 @@
     return result ?: value
 }
 
-private val TMP_MESSAGE: LogMessage by lazy { LogMessageImpl.Factory.create() }
-
-private inline fun Logger?.tryLog(
-    tag: String,
-    level: LogLevel,
-    messageInitializer: MessageInitializer,
-    noinline messagePrinter: MessagePrinter,
-    ex: Throwable? = null,
-) {
-    if (this != null) {
-        // Wrap messagePrinter to convert it from crossinline to noinline
-        this.log(level, messagePrinter, ex, messageInitializer)
-    } else {
-        messageInitializer(TMP_MESSAGE)
-        val msg = messagePrinter(TMP_MESSAGE)
-        when (level) {
-            LogLevel.VERBOSE -> Log.v(tag, msg, ex)
-            LogLevel.DEBUG -> Log.d(tag, msg, ex)
-            LogLevel.INFO -> Log.i(tag, msg, ex)
-            LogLevel.WARNING -> Log.w(tag, msg, ex)
-            LogLevel.ERROR -> Log.e(tag, msg, ex)
-            LogLevel.WTF -> Log.wtf(tag, msg, ex)
-        }
-    }
-}
-
 /** ClockRegistry aggregates providers and plugins */
 open class ClockRegistry(
     val context: Context,
@@ -114,12 +84,15 @@
     val handleAllUsers: Boolean,
     defaultClockProvider: ClockProvider,
     val fallbackClockId: ClockId = DEFAULT_CLOCK_ID,
-    messageBuffer: MessageBuffer? = null,
+    val clockBuffers: ClockMessageBuffers? = null,
     val keepAllLoaded: Boolean,
     subTag: String,
     var isTransitClockEnabled: Boolean = false,
 ) {
     private val TAG = "${ClockRegistry::class.simpleName} ($subTag)"
+    private val logger: Logger =
+        Logger(clockBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.DEBUG), TAG)
+
     interface ClockChangeListener {
         // Called when the active clock changes
         fun onCurrentClockChanged() {}
@@ -128,7 +101,6 @@
         fun onAvailableClocksChanged() {}
     }
 
-    private val logger: Logger? = if (messageBuffer != null) Logger(messageBuffer, TAG) else null
     private val availableClocks = ConcurrentHashMap<ClockId, ClockInfo>()
     private val clockChangeListeners = mutableListOf<ClockChangeListener>()
     private val settingObserver =
@@ -157,21 +129,15 @@
 
                 val knownClocks = KNOWN_PLUGINS.get(manager.getPackage())
                 if (knownClocks == null) {
-                    logger.tryLog(
-                        TAG,
-                        LogLevel.WARNING,
-                        { str1 = manager.getPackage() },
-                        { "Loading unrecognized clock package: $str1" }
-                    )
+                    logger.w({ "Loading unrecognized clock package: $str1" }) {
+                        str1 = manager.getPackage()
+                    }
                     return true
                 }
 
-                logger.tryLog(
-                    TAG,
-                    LogLevel.INFO,
-                    { str1 = manager.getPackage() },
-                    { "Skipping initial load of known clock package package: $str1" }
-                )
+                logger.i({ "Skipping initial load of known clock package package: $str1" }) {
+                    str1 = manager.getPackage()
+                }
 
                 var isCurrentClock = false
                 var isClockListChanged = false
@@ -185,19 +151,14 @@
                         }
 
                     if (manager != info.manager) {
-                        logger.tryLog(
-                            TAG,
-                            LogLevel.ERROR,
-                            {
-                                str1 = id
-                                str2 = info.manager.toString()
-                                str3 = manager.toString()
-                            },
-                            {
-                                "Clock Id conflict on attach: " +
-                                    "$str1 is double registered by $str2 and $str3"
-                            }
-                        )
+                        logger.e({
+                            "Clock Id conflict on attach: " +
+                                "$str1 is double registered by $str2 and $str3"
+                        }) {
+                            str1 = id
+                            str2 = info.manager.toString()
+                            str3 = manager.toString()
+                        }
                         continue
                     }
 
@@ -219,6 +180,8 @@
                 pluginContext: Context,
                 manager: PluginLifecycleManager<ClockProviderPlugin>
             ) {
+                plugin.initialize(clockBuffers)
+
                 var isClockListChanged = false
                 for (clock in plugin.getClocks()) {
                     val id = clock.clockId
@@ -233,19 +196,14 @@
                         }
 
                     if (manager != info.manager) {
-                        logger.tryLog(
-                            TAG,
-                            LogLevel.ERROR,
-                            {
-                                str1 = id
-                                str2 = info.manager.toString()
-                                str3 = manager.toString()
-                            },
-                            {
-                                "Clock Id conflict on load: " +
-                                    "$str1 is double registered by $str2 and $str3"
-                            }
-                        )
+                        logger.e({
+                            "Clock Id conflict on load: " +
+                                "$str1 is double registered by $str2 and $str3"
+                        }) {
+                            str1 = id
+                            str2 = info.manager.toString()
+                            str3 = manager.toString()
+                        }
                         manager.unloadPlugin()
                         continue
                     }
@@ -268,19 +226,14 @@
                     val id = clock.clockId
                     val info = availableClocks[id]
                     if (info?.manager != manager) {
-                        logger.tryLog(
-                            TAG,
-                            LogLevel.ERROR,
-                            {
-                                str1 = id
-                                str2 = info?.manager.toString()
-                                str3 = manager.toString()
-                            },
-                            {
-                                "Clock Id conflict on unload: " +
-                                    "$str1 is double registered by $str2 and $str3"
-                            }
-                        )
+                        logger.e({
+                            "Clock Id conflict on unload: " +
+                                "$str1 is double registered by $str2 and $str3"
+                        }) {
+                            str1 = id
+                            str2 = info?.manager.toString()
+                            str3 = manager.toString()
+                        }
                         continue
                     }
                     info.provider = null
@@ -350,7 +303,7 @@
 
                 ClockSettings.deserialize(json)
             } catch (ex: Exception) {
-                logger.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to parse clock settings" }, ex)
+                logger.e("Failed to parse clock settings", ex)
                 null
             }
         settings = result
@@ -379,7 +332,7 @@
                 )
             }
         } catch (ex: Exception) {
-            logger.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to set clock settings" }, ex)
+            logger.e("Failed to set clock settings", ex)
         }
         settings = value
     }
@@ -451,7 +404,8 @@
         }
 
     init {
-        // Register default clock designs
+        // Initialize & register default clock designs
+        defaultClockProvider.initialize(clockBuffers)
         for (clock in defaultClockProvider.getClocks()) {
             availableClocks[clock.clockId] = ClockInfo(clock, defaultClockProvider, null)
         }
@@ -514,12 +468,7 @@
     fun verifyLoadedProviders() {
         val shouldSchedule = isQueued.compareAndSet(false, true)
         if (!shouldSchedule) {
-            logger.tryLog(
-                TAG,
-                LogLevel.VERBOSE,
-                {},
-                { "verifyLoadedProviders: shouldSchedule=false" }
-            )
+            logger.v("verifyLoadedProviders: shouldSchedule=false")
             return
         }
 
@@ -528,12 +477,7 @@
             synchronized(availableClocks) {
                 isQueued.set(false)
                 if (keepAllLoaded) {
-                    logger.tryLog(
-                        TAG,
-                        LogLevel.INFO,
-                        {},
-                        { "verifyLoadedProviders: keepAllLoaded=true" }
-                    )
+                    logger.i("verifyLoadedProviders: keepAllLoaded=true")
                     // Enforce that all plugins are loaded if requested
                     for ((_, info) in availableClocks) {
                         info.manager?.loadPlugin()
@@ -543,12 +487,7 @@
 
                 val currentClock = availableClocks[currentClockId]
                 if (currentClock == null) {
-                    logger.tryLog(
-                        TAG,
-                        LogLevel.INFO,
-                        {},
-                        { "verifyLoadedProviders: currentClock=null" }
-                    )
+                    logger.i("verifyLoadedProviders: currentClock=null")
                     // Current Clock missing, load no plugins and use default
                     for ((_, info) in availableClocks) {
                         info.manager?.unloadPlugin()
@@ -556,12 +495,7 @@
                     return@launch
                 }
 
-                logger.tryLog(
-                    TAG,
-                    LogLevel.INFO,
-                    {},
-                    { "verifyLoadedProviders: load currentClock" }
-                )
+                logger.i("verifyLoadedProviders: load currentClock")
                 val currentManager = currentClock.manager
                 currentManager?.loadPlugin()
 
@@ -577,30 +511,26 @@
 
     private fun onConnected(info: ClockInfo) {
         val isCurrent = currentClockId == info.metadata.clockId
-        logger.tryLog(
-            TAG,
+        logger.log(
             if (isCurrent) LogLevel.INFO else LogLevel.DEBUG,
-            {
-                str1 = info.metadata.clockId
-                str2 = info.manager.toString()
-                bool1 = isCurrent
-            },
             { "Connected $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
-        )
+        ) {
+            str1 = info.metadata.clockId
+            str2 = info.manager.toString()
+            bool1 = isCurrent
+        }
     }
 
     private fun onLoaded(info: ClockInfo) {
         val isCurrent = currentClockId == info.metadata.clockId
-        logger.tryLog(
-            TAG,
+        logger.log(
             if (isCurrent) LogLevel.INFO else LogLevel.DEBUG,
-            {
-                str1 = info.metadata.clockId
-                str2 = info.manager.toString()
-                bool1 = isCurrent
-            },
             { "Loaded $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
-        )
+        ) {
+            str1 = info.metadata.clockId
+            str2 = info.manager.toString()
+            bool1 = isCurrent
+        }
 
         if (isCurrent) {
             triggerOnCurrentClockChanged()
@@ -609,16 +539,14 @@
 
     private fun onUnloaded(info: ClockInfo) {
         val isCurrent = currentClockId == info.metadata.clockId
-        logger.tryLog(
-            TAG,
+        logger.log(
             if (isCurrent) LogLevel.WARNING else LogLevel.DEBUG,
-            {
-                str1 = info.metadata.clockId
-                str2 = info.manager.toString()
-                bool1 = isCurrent
-            },
             { "Unloaded $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
-        )
+        ) {
+            str1 = info.metadata.clockId
+            str2 = info.manager.toString()
+            bool1 = isCurrent
+        }
 
         if (isCurrent) {
             triggerOnCurrentClockChanged()
@@ -627,16 +555,14 @@
 
     private fun onDisconnected(info: ClockInfo) {
         val isCurrent = currentClockId == info.metadata.clockId
-        logger.tryLog(
-            TAG,
+        logger.log(
             if (isCurrent) LogLevel.INFO else LogLevel.DEBUG,
-            {
-                str1 = info.metadata.clockId
-                str2 = info.manager.toString()
-                bool1 = isCurrent
-            },
             { "Disconnected $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
-        )
+        ) {
+            str1 = info.metadata.clockId
+            str2 = info.manager.toString()
+            bool1 = isCurrent
+        }
     }
 
     fun getClocks(): List<ClockMetadata> {
@@ -676,23 +602,13 @@
         if (isEnabled && clockId.isNotEmpty()) {
             val clock = createClock(clockId)
             if (clock != null) {
-                logger.tryLog(TAG, LogLevel.INFO, { str1 = clockId }, { "Rendering clock $str1" })
+                logger.i({ "Rendering clock $str1" }) { str1 = clockId }
                 return clock
             } else if (availableClocks.containsKey(clockId)) {
-                logger.tryLog(
-                    TAG,
-                    LogLevel.WARNING,
-                    { str1 = clockId },
-                    { "Clock $str1 not loaded; using default" }
-                )
+                logger.w({ "Clock $str1 not loaded; using default" }) { str1 = clockId }
                 verifyLoadedProviders()
             } else {
-                logger.tryLog(
-                    TAG,
-                    LogLevel.ERROR,
-                    { str1 = clockId },
-                    { "Clock $str1 not found; using default" }
-                )
+                logger.e({ "Clock $str1 not found; using default" }) { str1 = clockId }
             }
         }
 
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 01c03b1..99d3216 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceController
 import com.android.systemui.plugins.clocks.ClockFaceEvents
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockSettings
 import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
 import com.android.systemui.plugins.clocks.WeatherData
@@ -41,8 +42,6 @@
 import java.util.Locale
 import java.util.TimeZone
 
-private val TAG = DefaultClockController::class.simpleName
-
 /**
  * Controls the default clock visuals.
  *
@@ -56,6 +55,7 @@
     private val settings: ClockSettings?,
     private val hasStepClockAnimation: Boolean = false,
     private val migratedClocks: Boolean = false,
+    messageBuffers: ClockMessageBuffers? = null,
 ) : ClockController {
     override val smallClock: DefaultClockFaceController
     override val largeClock: LargeClockFaceController
@@ -83,13 +83,15 @@
             DefaultClockFaceController(
                 layoutInflater.inflate(R.layout.clock_default_small, parent, false)
                     as AnimatableClockView,
-                settings?.seedColor
+                settings?.seedColor,
+                messageBuffers?.smallClockMessageBuffer
             )
         largeClock =
             LargeClockFaceController(
                 layoutInflater.inflate(R.layout.clock_default_large, parent, false)
                     as AnimatableClockView,
-                settings?.seedColor
+                settings?.seedColor,
+                messageBuffers?.largeClockMessageBuffer
             )
         clocks = listOf(smallClock.view, largeClock.view)
 
@@ -110,6 +112,7 @@
     open inner class DefaultClockFaceController(
         override val view: AnimatableClockView,
         var seedColor: Int?,
+        messageBuffer: MessageBuffer?,
     ) : ClockFaceController {
 
         // MAGENTA is a placeholder, and will be assigned correctly in initialize
@@ -120,12 +123,6 @@
         override val config = ClockFaceConfig()
         override val layout = DefaultClockFaceLayout(view)
 
-        override var messageBuffer: MessageBuffer?
-            get() = view.messageBuffer
-            set(value) {
-                view.messageBuffer = value
-            }
-
         override var animations: DefaultClockAnimations = DefaultClockAnimations(view, 0f, 0f)
             internal set
 
@@ -134,6 +131,7 @@
                 currentColor = seedColor!!
             }
             view.setColors(DOZE_COLOR, currentColor)
+            messageBuffer?.let { view.messageBuffer = it }
         }
 
         override val events =
@@ -188,7 +186,8 @@
     inner class LargeClockFaceController(
         view: AnimatableClockView,
         seedColor: Int?,
-    ) : DefaultClockFaceController(view, seedColor) {
+        messageBuffer: MessageBuffer?,
+    ) : DefaultClockFaceController(view, seedColor, messageBuffer) {
         override val layout = DefaultClockFaceLayout(view)
         override val config =
             ClockFaceConfig(hasCustomPositionUpdatedAnimation = hasStepClockAnimation)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index a219be5..20f87a0 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.customization.R
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockId
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockMetadata
 import com.android.systemui.plugins.clocks.ClockProvider
 import com.android.systemui.plugins.clocks.ClockSettings
@@ -35,6 +36,12 @@
     val hasStepClockAnimation: Boolean = false,
     val migratedClocks: Boolean = false
 ) : ClockProvider {
+    private var messageBuffers: ClockMessageBuffers? = null
+
+    override fun initialize(buffers: ClockMessageBuffers?) {
+        messageBuffers = buffers
+    }
+
     override fun getClocks(): List<ClockMetadata> = listOf(ClockMetadata(DEFAULT_CLOCK_ID))
 
     override fun createClock(settings: ClockSettings): ClockController {
@@ -49,6 +56,7 @@
             settings,
             hasStepClockAnimation,
             migratedClocks,
+            messageBuffers,
         )
     }
 
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt
new file mode 100644
index 0000000..006b521
--- /dev/null
+++ b/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.log.core
+
+import android.util.Log
+import com.android.systemui.log.LogMessageImpl
+
+/**
+ * A simple implementation of [MessageBuffer] that forwards messages to [android.util.Log]
+ * immediately. This defeats the intention behind [LogBuffer] and should only be used when
+ * [LogBuffer]s are unavailable in a certain context.
+ */
+class LogcatOnlyMessageBuffer(
+    val targetLogLevel: LogLevel,
+) : MessageBuffer {
+    private val singleMessage = LogMessageImpl.Factory.create()
+    private var isObtained: Boolean = false
+
+    @Synchronized
+    override fun obtain(
+        tag: String,
+        level: LogLevel,
+        messagePrinter: MessagePrinter,
+        exception: Throwable?,
+    ): LogMessage {
+        if (isObtained) {
+            throw UnsupportedOperationException(
+                "Message has already been obtained. Call order is incorrect."
+            )
+        }
+
+        singleMessage.reset(tag, level, System.currentTimeMillis(), messagePrinter, exception)
+        isObtained = true
+        return singleMessage
+    }
+
+    @Synchronized
+    override fun commit(message: LogMessage) {
+        if (singleMessage != message) {
+            throw IllegalArgumentException("Message argument is not the expected message.")
+        }
+        if (!isObtained) {
+            throw UnsupportedOperationException(
+                "Message has not been obtained. Call order is incorrect."
+            )
+        }
+
+        if (message.level >= targetLogLevel) {
+            val strMessage = message.messagePrinter(message)
+            when (message.level) {
+                LogLevel.VERBOSE -> Log.v(message.tag, strMessage, message.exception)
+                LogLevel.DEBUG -> Log.d(message.tag, strMessage, message.exception)
+                LogLevel.INFO -> Log.i(message.tag, strMessage, message.exception)
+                LogLevel.WARNING -> Log.w(message.tag, strMessage, message.exception)
+                LogLevel.ERROR -> Log.e(message.tag, strMessage, message.exception)
+                LogLevel.WTF -> Log.wtf(message.tag, strMessage, message.exception)
+            }
+        }
+
+        isObtained = false
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index a726b7c..b0beab9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -47,6 +47,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
@@ -54,7 +55,6 @@
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.util.settings.SecureSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import org.junit.Before
@@ -101,7 +101,6 @@
     @Mock
     private lateinit var unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController
     @Mock private lateinit var udfpsDisplayMode: UdfpsDisplayModeProvider
-    @Mock private lateinit var secureSettings: SecureSettings
     @Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback
     @Mock private lateinit var udfpsController: UdfpsController
     @Mock private lateinit var udfpsView: UdfpsView
@@ -117,6 +116,7 @@
     @Mock
     private lateinit var udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate
     @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+    @Mock private lateinit var shadeInteractor: ShadeInteractor
     @Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
 
     private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
@@ -174,6 +174,7 @@
                 mSelectedUserInteractor,
                 { deviceEntryUdfpsTouchOverlayViewModel },
                 { defaultUdfpsTouchOverlayViewModel },
+                shadeInteractor
             )
         block()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index a4b55e7..e5da1f8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -94,6 +94,7 @@
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -205,6 +206,8 @@
     @Mock
     private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
     @Mock
+    private ShadeInteractor mShadeInteractor;
+    @Mock
     private SinglePointerTouchProcessor mSinglePointerTouchProcessor;
     @Mock
     private SessionTracker mSessionTracker;
@@ -328,6 +331,7 @@
                 mActivityLaunchAnimator,
                 mBiometricExecutor,
                 mPrimaryBouncerInteractor,
+                mShadeInteractor,
                 mSinglePointerTouchProcessor,
                 mSessionTracker,
                 mAlternateBouncerInteractor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
index ac16c13..13b53a8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
@@ -36,6 +36,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.SystemUIDialogManager;
@@ -70,6 +71,7 @@
     protected @Mock UdfpsController mUdfpsController;
     protected @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
     protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+    protected @Mock ShadeInteractor mShadeInteractor;
     protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor;
     protected @Mock UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
     protected @Mock SelectedUserInteractor mSelectedUserInteractor;
@@ -149,7 +151,8 @@
                 mAlternateBouncerInteractor,
                 mUdfpsKeyguardAccessibilityDelegate,
                 mSelectedUserInteractor,
-                mKeyguardTransitionInteractor);
+                mKeyguardTransitionInteractor,
+                mShadeInteractor);
         return controller;
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index 182712a..ddaa488 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -208,11 +208,11 @@
             val repository = initCommunalWidgetRepository()
             runCurrent()
 
-            val ids = listOf(104, 103, 101)
-            repository.updateWidgetOrder(ids)
+            val widgetIdToPriorityMap = mapOf(104 to 1, 103 to 2, 101 to 3)
+            repository.updateWidgetOrder(widgetIdToPriorityMap)
             runCurrent()
 
-            verify(communalWidgetDao).updateWidgetOrder(ids)
+            verify(communalWidgetDao).updateWidgetOrder(widgetIdToPriorityMap)
         }
 
     @Test
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index 1c5f221..4436be7 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -41,16 +41,13 @@
 
 /** Interface for building clocks and providing information about those clocks */
 interface ClockProvider {
+    /** Initializes the clock provider with debug log buffers */
+    fun initialize(buffers: ClockMessageBuffers?)
+
     /** Returns metadata for all clocks this provider knows about */
     fun getClocks(): List<ClockMetadata>
 
     /** Initializes and returns the target clock design */
-    @Deprecated("Use overload with ClockSettings")
-    fun createClock(id: ClockId): ClockController {
-        return createClock(ClockSettings(id, null))
-    }
-
-    /** Initializes and returns the target clock design */
     fun createClock(settings: ClockSettings): ClockController
 
     /** A static thumbnail for rendering in some examples */
@@ -98,11 +95,20 @@
 
     /** Triggers for various animations */
     val animations: ClockAnimations
-
-    /** Some clocks may log debug information */
-    var messageBuffer: MessageBuffer?
 }
 
+/** For clocks that want to report debug information */
+data class ClockMessageBuffers(
+    /** Message buffer for general infra */
+    val infraMessageBuffer: MessageBuffer,
+
+    /** Message buffer for small clock renering */
+    val smallClockMessageBuffer: MessageBuffer,
+
+    /** Message buffer for large clock rendering */
+    val largeClockMessageBuffer: MessageBuffer,
+)
+
 /** Specifies layout information for the */
 interface ClockFaceLayout {
     /** All clock views to add to the root constraint layout before applying constraints. */
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 76abad8..bcc2044 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -45,12 +45,11 @@
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
 import com.android.systemui.log.core.LogLevel.DEBUG
-import com.android.systemui.log.dagger.KeyguardLargeClockLog
-import com.android.systemui.log.dagger.KeyguardSmallClockLog
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockFaceController
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockTickRate
 import com.android.systemui.plugins.clocks.AlarmData
 import com.android.systemui.plugins.clocks.WeatherData
@@ -91,117 +90,120 @@
     private val context: Context,
     @Main private val mainExecutor: DelayableExecutor,
     @Background private val bgExecutor: Executor,
-    @KeyguardSmallClockLog private val smallLogBuffer: LogBuffer?,
-    @KeyguardLargeClockLog private val largeLogBuffer: LogBuffer?,
+    private val clockBuffers: ClockMessageBuffers,
     private val featureFlags: FeatureFlags,
     private val zenModeController: ZenModeController,
 ) {
+    var loggers = listOf(
+        clockBuffers.infraMessageBuffer,
+        clockBuffers.smallClockMessageBuffer,
+        clockBuffers.largeClockMessageBuffer
+    ).map { Logger(it, TAG) }
+
     var clock: ClockController? = null
+        get() = field
         set(value) {
-            smallClockOnAttachStateChangeListener?.let {
-                field?.smallClock?.view?.removeOnAttachStateChangeListener(it)
+            disconnectClock(field)
+            field = value
+            connectClock(value)
+        }
+
+    private fun disconnectClock(clock: ClockController?) {
+        if (clock == null) { return; }
+        smallClockOnAttachStateChangeListener?.let {
+            clock.smallClock.view.removeOnAttachStateChangeListener(it)
+            smallClockFrame?.viewTreeObserver
+                    ?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
+        }
+        largeClockOnAttachStateChangeListener?.let {
+            clock.largeClock.view.removeOnAttachStateChangeListener(it)
+        }
+    }
+
+    private fun connectClock(clock: ClockController?) {
+        if (clock == null) { return; }
+        val clockStr = clock.toString()
+        loggers.forEach { it.d({ "New Clock: $str1" }) { str1 = clockStr } }
+
+        clock.initialize(resources, dozeAmount, 0f)
+
+        if (!regionSamplingEnabled) {
+            updateColors()
+        } else {
+            smallRegionSampler = createRegionSampler(
+                clock.smallClock.view,
+                mainExecutor,
+                bgExecutor,
+                regionSamplingEnabled,
+                isLockscreen = true,
+                ::updateColors
+            ).apply { startRegionSampler() }
+
+            largeRegionSampler = createRegionSampler(
+                clock.largeClock.view,
+                mainExecutor,
+                bgExecutor,
+                regionSamplingEnabled,
+                isLockscreen = true,
+                ::updateColors
+            ).apply { startRegionSampler() }
+
+            updateColors()
+        }
+        updateFontSizes()
+        updateTimeListeners()
+
+        weatherData?.let {
+            if (WeatherData.DEBUG) {
+                Log.i(TAG, "Pushing cached weather data to new clock: $it")
+            }
+            clock.events.onWeatherDataChanged(it)
+        }
+        zenData?.let {
+            clock.events.onZenDataChanged(it)
+        }
+        alarmData?.let {
+            clock.events.onAlarmDataChanged(it)
+        }
+
+        smallClockOnAttachStateChangeListener = object : OnAttachStateChangeListener {
+            var pastVisibility: Int? = null
+            override fun onViewAttachedToWindow(view: View) {
+                clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+                // Match the asing for view.parent's layout classes.
+                smallClockFrame = (view.parent as ViewGroup)?.also { frame ->
+                    pastVisibility = frame.visibility
+                    onGlobalLayoutListener = OnGlobalLayoutListener {
+                        val currentVisibility = frame.visibility
+                        if (pastVisibility != currentVisibility) {
+                            pastVisibility = currentVisibility
+                            // when small clock is visible,
+                            // recalculate bounds and sample
+                            if (currentVisibility == View.VISIBLE) {
+                                smallRegionSampler?.stopRegionSampler()
+                                smallRegionSampler?.startRegionSampler()
+                            }
+                        }
+                    }
+                    frame.viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListener)
+                }
+            }
+
+            override fun onViewDetachedFromWindow(p0: View) {
                 smallClockFrame?.viewTreeObserver
                         ?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
             }
-            largeClockOnAttachStateChangeListener?.let {
-                field?.largeClock?.view?.removeOnAttachStateChangeListener(it)
-            }
-
-            field = value
-            if (value != null) {
-                smallLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" })
-                value.smallClock.messageBuffer = smallLogBuffer
-                largeLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" })
-                value.largeClock.messageBuffer = largeLogBuffer
-
-                value.initialize(resources, dozeAmount, 0f)
-
-                if (!regionSamplingEnabled) {
-                    updateColors()
-                } else {
-                    clock?.let {
-                        smallRegionSampler = createRegionSampler(
-                                it.smallClock.view,
-                                mainExecutor,
-                                bgExecutor,
-                                regionSamplingEnabled,
-                                isLockscreen = true,
-                                ::updateColors
-                        )?.apply { startRegionSampler() }
-
-                        largeRegionSampler = createRegionSampler(
-                                it.largeClock.view,
-                                mainExecutor,
-                                bgExecutor,
-                                regionSamplingEnabled,
-                                isLockscreen = true,
-                                ::updateColors
-                        )?.apply { startRegionSampler() }
-
-                        updateColors()
-                    }
-                }
-                updateFontSizes()
-                updateTimeListeners()
-                weatherData?.let {
-                    if (WeatherData.DEBUG) {
-                        Log.i(TAG, "Pushing cached weather data to new clock: $it")
-                    }
-                    value.events.onWeatherDataChanged(it)
-                }
-                zenData?.let {
-                    value.events.onZenDataChanged(it)
-                }
-                alarmData?.let {
-                    value.events.onAlarmDataChanged(it)
-                }
-
-                smallClockOnAttachStateChangeListener =
-                    object : OnAttachStateChangeListener {
-                        var pastVisibility: Int? = null
-                        override fun onViewAttachedToWindow(view: View) {
-                            value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
-                            // Match the asing for view.parent's layout classes.
-                            smallClockFrame = view.parent as ViewGroup
-                            smallClockFrame?.let { frame ->
-                                pastVisibility = frame.visibility
-                                onGlobalLayoutListener = OnGlobalLayoutListener {
-                                    val currentVisibility = frame.visibility
-                                    if (pastVisibility != currentVisibility) {
-                                        pastVisibility = currentVisibility
-                                        // when small clock is visible,
-                                        // recalculate bounds and sample
-                                        if (currentVisibility == View.VISIBLE) {
-                                            smallRegionSampler?.stopRegionSampler()
-                                            smallRegionSampler?.startRegionSampler()
-                                        }
-                                    }
-                                }
-                                frame.viewTreeObserver
-                                        .addOnGlobalLayoutListener(onGlobalLayoutListener)
-                            }
-                        }
-
-                        override fun onViewDetachedFromWindow(p0: View) {
-                            smallClockFrame?.viewTreeObserver
-                                    ?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
-                        }
-                }
-                value.smallClock.view
-                        .addOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
-
-                largeClockOnAttachStateChangeListener =
-                    object : OnAttachStateChangeListener {
-                        override fun onViewAttachedToWindow(p0: View) {
-                            value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
-                        }
-                        override fun onViewDetachedFromWindow(p0: View) {
-                        }
-                }
-                value.largeClock.view
-                        .addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
-            }
         }
+        clock.smallClock.view.addOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
+
+        largeClockOnAttachStateChangeListener = object : OnAttachStateChangeListener {
+            override fun onViewAttachedToWindow(p0: View) {
+                clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+            }
+            override fun onViewDetachedFromWindow(p0: View) {}
+        }
+        clock.largeClock.view.addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
+    }
 
     @VisibleForTesting
     var smallClockOnAttachStateChangeListener: OnAttachStateChangeListener? = null
@@ -247,6 +249,7 @@
             largeClock.events.onRegionDarknessChanged(isRegionDark)
         }
     }
+
     protected open fun createRegionSampler(
         sampledView: View,
         mainExecutor: Executor?,
@@ -254,7 +257,7 @@
         regionSamplingEnabled: Boolean,
         isLockscreen: Boolean,
         updateColors: () -> Unit
-    ): RegionSampler? {
+    ): RegionSampler {
         return RegionSampler(
             sampledView,
             mainExecutor,
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 661ce2c..878a5d8 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -28,9 +28,8 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
-import com.android.systemui.log.LogBuffer;
-import com.android.systemui.log.dagger.KeyguardClockLog;
 import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.plugins.clocks.ClockMessageBuffers;
 import com.android.systemui.res.R;
 import com.android.systemui.shared.clocks.ClockRegistry;
 import com.android.systemui.shared.clocks.DefaultClockProvider;
@@ -56,7 +55,7 @@
             FeatureFlags featureFlags,
             @Main Resources resources,
             LayoutInflater layoutInflater,
-            @KeyguardClockLog LogBuffer logBuffer) {
+            ClockMessageBuffers clockBuffers) {
         ClockRegistry registry = new ClockRegistry(
                 context,
                 pluginManager,
@@ -72,7 +71,7 @@
                         featureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION),
                         migrateClocksToBlueprint()),
                 context.getString(R.string.lockscreen_clock_id_fallback),
-                logBuffer,
+                clockBuffers,
                 /* keepAllLoaded = */ false,
                 /* subTag = */ "System",
                 /* isTransitClockEnabled = */ featureFlags.isEnabled(Flags.TRANSIT_CLOCK));
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
index 3cb6314..3ca95e1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
@@ -77,7 +77,7 @@
     @VisibleForTesting
     SparseArray<SparseArray<Float>> mUsersScales = new SparseArray();
 
-    private static class ControllerSupplier extends
+    private static class WindowMagnificationControllerSupplier extends
             DisplayIdIndexSupplier<WindowMagnificationController> {
 
         private final Context mContext;
@@ -86,7 +86,7 @@
         private final SysUiState mSysUiState;
         private final SecureSettings mSecureSettings;
 
-        ControllerSupplier(Context context, Handler handler,
+        WindowMagnificationControllerSupplier(Context context, Handler handler,
                 WindowMagnifierCallback windowMagnifierCallback,
                 DisplayManager displayManager, SysUiState sysUiState,
                 SecureSettings secureSettings) {
@@ -118,7 +118,7 @@
     }
 
     @VisibleForTesting
-    DisplayIdIndexSupplier<WindowMagnificationController> mMagnificationControllerSupplier;
+    DisplayIdIndexSupplier<WindowMagnificationController> mWindowMagnificationControllerSupplier;
 
     private static class SettingsSupplier extends
             DisplayIdIndexSupplier<MagnificationSettingsController> {
@@ -168,7 +168,7 @@
         mOverviewProxyService = overviewProxyService;
         mDisplayTracker = displayTracker;
         mA11yLogger = a11yLogger;
-        mMagnificationControllerSupplier = new ControllerSupplier(context,
+        mWindowMagnificationControllerSupplier = new WindowMagnificationControllerSupplier(context,
                 mHandler, mWindowMagnifierCallback,
                 displayManager, sysUiState, secureSettings);
         mMagnificationSettingsSupplier = new SettingsSupplier(context,
@@ -196,7 +196,8 @@
     private void updateSysUiStateFlag() {
         //TODO(b/187510533): support multi-display once SysuiState supports it.
         final WindowMagnificationController controller =
-                mMagnificationControllerSupplier.valueAt(mDisplayTracker.getDefaultDisplayId());
+                mWindowMagnificationControllerSupplier.valueAt(
+                        mDisplayTracker.getDefaultDisplayId());
         if (controller != null) {
             controller.updateSysUIStateFlag();
         } else {
@@ -212,7 +213,7 @@
             float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
             @Nullable IRemoteMagnificationAnimationCallback callback) {
         final WindowMagnificationController windowMagnificationController =
-                mMagnificationControllerSupplier.get(displayId);
+                mWindowMagnificationControllerSupplier.get(displayId);
         if (windowMagnificationController != null) {
             windowMagnificationController.enableWindowMagnification(scale, centerX, centerY,
                     magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, callback);
@@ -222,7 +223,7 @@
     @MainThread
     void setScaleForWindowMagnification(int displayId, float scale) {
         final WindowMagnificationController windowMagnificationController =
-                mMagnificationControllerSupplier.get(displayId);
+                mWindowMagnificationControllerSupplier.get(displayId);
         if (windowMagnificationController != null) {
             windowMagnificationController.setScale(scale);
         }
@@ -231,7 +232,7 @@
     @MainThread
     void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
         final WindowMagnificationController windowMagnificationcontroller =
-                mMagnificationControllerSupplier.get(displayId);
+                mWindowMagnificationControllerSupplier.get(displayId);
         if (windowMagnificationcontroller != null) {
             windowMagnificationcontroller.moveWindowMagnifier(offsetX, offsetY);
         }
@@ -241,7 +242,7 @@
     void moveWindowMagnifierToPositionInternal(int displayId, float positionX, float positionY,
             IRemoteMagnificationAnimationCallback callback) {
         final WindowMagnificationController windowMagnificationController =
-                mMagnificationControllerSupplier.get(displayId);
+                mWindowMagnificationControllerSupplier.get(displayId);
         if (windowMagnificationController != null) {
             windowMagnificationController.moveWindowMagnifierToPosition(positionX, positionY,
                     callback);
@@ -252,7 +253,7 @@
     void disableWindowMagnification(int displayId,
             @Nullable IRemoteMagnificationAnimationCallback callback) {
         final WindowMagnificationController windowMagnificationController =
-                mMagnificationControllerSupplier.get(displayId);
+                mWindowMagnificationControllerSupplier.get(displayId);
         if (windowMagnificationController != null) {
             windowMagnificationController.deleteWindowMagnification(callback);
         }
@@ -417,7 +418,7 @@
     @MainThread
     private void onSetMagnifierSizeInternal(int displayId, int index) {
         final WindowMagnificationController windowMagnificationController =
-                mMagnificationControllerSupplier.get(displayId);
+                mWindowMagnificationControllerSupplier.get(displayId);
         if (windowMagnificationController != null) {
             windowMagnificationController.changeMagnificationSize(index);
         }
@@ -426,7 +427,7 @@
     @MainThread
     private void onSetDiagonalScrollingInternal(int displayId, boolean enable) {
         final WindowMagnificationController windowMagnificationController =
-                mMagnificationControllerSupplier.get(displayId);
+                mWindowMagnificationControllerSupplier.get(displayId);
         if (windowMagnificationController != null) {
             windowMagnificationController.setDiagonalScrolling(enable);
         }
@@ -435,7 +436,7 @@
     @MainThread
     private void onEditMagnifierSizeModeInternal(int displayId, boolean enable) {
         final WindowMagnificationController windowMagnificationController =
-                mMagnificationControllerSupplier.get(displayId);
+                mWindowMagnificationControllerSupplier.get(displayId);
         if (windowMagnificationController != null && windowMagnificationController.isActivated()) {
             windowMagnificationController.setEditMagnifierSizeMode(enable);
         }
@@ -444,7 +445,7 @@
     @MainThread
     private void onModeSwitchInternal(int displayId, int newMode) {
         final WindowMagnificationController windowMagnificationController =
-                mMagnificationControllerSupplier.get(displayId);
+                mWindowMagnificationControllerSupplier.get(displayId);
         final boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
         final boolean isSwitchToWindowMode = (newMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
         final boolean changed = isSwitchToWindowMode ^ isWindowMagnifierActivated;
@@ -463,7 +464,7 @@
     @MainThread
     private void onSettingsPanelVisibilityChangedInternal(int displayId, boolean shown) {
         final WindowMagnificationController windowMagnificationController =
-                mMagnificationControllerSupplier.get(displayId);
+                mWindowMagnificationControllerSupplier.get(displayId);
         if (windowMagnificationController != null) {
             boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
             if (isWindowMagnifierActivated) {
@@ -495,7 +496,7 @@
     @Override
     public void dump(PrintWriter pw, String[] args) {
         pw.println(TAG);
-        mMagnificationControllerSupplier.forEach(
+        mWindowMagnificationControllerSupplier.forEach(
                 magnificationController -> magnificationController.dump(pw));
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index ab23564..83d415f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -403,6 +403,13 @@
 
             final BiometricPromptLayout view = (BiometricPromptLayout) layoutInflater.inflate(
                     R.layout.biometric_prompt_layout, null, false);
+            /**
+             * View is only set visible in BiometricViewSizeBinder once PromptSize is determined
+             * that accounts for iconView size, to prevent prompt resizing being visible to the
+             * user.
+             * TODO(b/288175072): May be able to remove this once constraint layout is implemented
+             */
+            view.setVisibility(View.INVISIBLE);
             mBiometricView = BiometricViewBinder.bind(view, viewModel, mPanelController,
                     // TODO(b/201510778): This uses the wrong timeout in some cases
                     getJankListener(view, TRANSIT,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
index 66fb8ca..7d9ec08 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
@@ -23,16 +23,14 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.animation.Interpolators
 import com.android.systemui.Dumpable
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import com.android.systemui.util.ViewController
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 import java.io.PrintWriter
 
@@ -49,7 +47,7 @@
 abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
     view: T,
     protected val statusBarStateController: StatusBarStateController,
-    protected val primaryBouncerInteractor: PrimaryBouncerInteractor,
+    protected val shadeInteractor: ShadeInteractor,
     protected val dialogManager: SystemUIDialogManager,
     private val dumpManager: DumpManager
 ) : ViewController<T>(view), Dumpable {
@@ -94,20 +92,18 @@
             // can make the view not visible; and we still want to listen for events
             // that may make the view visible again.
             repeatOnLifecycle(Lifecycle.State.CREATED) {
-                listenForBouncerExpansion(this)
+                listenForShadeExpansion(this)
             }
         }
     }
 
     @VisibleForTesting
-    open suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
+    suspend fun listenForShadeExpansion(scope: CoroutineScope): Job {
         return scope.launch {
-            primaryBouncerInteractor.bouncerExpansion.map { 1f - it }.collect { expansion: Float ->
-                if (statusBarStateController.state != SHADE) {
-                    notificationShadeVisible = expansion > 0f
-                    view.onExpansionChanged(expansion)
-                    updatePauseAuth()
-                }
+            shadeInteractor.anyExpansion.collect { expansion ->
+                notificationShadeVisible = expansion > 0f
+                view.onExpansionChanged(expansion)
+                updatePauseAuth()
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
index 03749a9..e7b0d9f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
@@ -15,9 +15,9 @@
  */
 package com.android.systemui.biometrics
 
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
 
 /**
@@ -26,13 +26,13 @@
 class UdfpsBpViewController(
     view: UdfpsBpView,
     statusBarStateController: StatusBarStateController,
-    primaryBouncerInteractor: PrimaryBouncerInteractor,
+    shadeInteractor: ShadeInteractor,
     systemUIDialogManager: SystemUIDialogManager,
     dumpManager: DumpManager
 ) : UdfpsAnimationViewController<UdfpsBpView>(
     view,
     statusBarStateController,
-    primaryBouncerInteractor,
+    shadeInteractor,
     systemUIDialogManager,
     dumpManager
 ) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 240728a..2fd13b3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -89,6 +89,7 @@
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.VibratorHelper;
@@ -162,6 +163,7 @@
     @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
     @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
     @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+    @NonNull private final ShadeInteractor mShadeInteractor;
     @Nullable private final TouchProcessor mTouchProcessor;
     @NonNull private final SessionTracker mSessionTracker;
     @NonNull private final Lazy<DeviceEntryUdfpsTouchOverlayViewModel>
@@ -290,7 +292,8 @@
                         mKeyguardTransitionInteractor,
                         mSelectedUserInteractor,
                         mDeviceEntryUdfpsTouchOverlayViewModel,
-                        mDefaultUdfpsTouchOverlayViewModel
+                        mDefaultUdfpsTouchOverlayViewModel,
+                        mShadeInteractor
                     )));
         }
 
@@ -656,6 +659,7 @@
             @NonNull ActivityLaunchAnimator activityLaunchAnimator,
             @NonNull @BiometricsBackground Executor biometricsExecutor,
             @NonNull PrimaryBouncerInteractor primaryBouncerInteractor,
+            @NonNull ShadeInteractor shadeInteractor,
             @NonNull SinglePointerTouchProcessor singlePointerTouchProcessor,
             @NonNull SessionTracker sessionTracker,
             @NonNull AlternateBouncerInteractor alternateBouncerInteractor,
@@ -705,6 +709,7 @@
 
         mBiometricExecutor = biometricsExecutor;
         mPrimaryBouncerInteractor = primaryBouncerInteractor;
+        mShadeInteractor = shadeInteractor;
         mAlternateBouncerInteractor = alternateBouncerInteractor;
         mInputManager = inputManager;
         mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index aabee93..b94a177 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -57,6 +57,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
@@ -107,6 +108,7 @@
     private val selectedUserInteractor: SelectedUserInteractor,
     private val deviceEntryUdfpsTouchOverlayViewModel: Lazy<DeviceEntryUdfpsTouchOverlayViewModel>,
     private val defaultUdfpsTouchOverlayViewModel: Lazy<DefaultUdfpsTouchOverlayViewModel>,
+    private val shadeInteractor: ShadeInteractor,
 ) {
     private var overlayViewLegacy: UdfpsView? = null
         private set
@@ -277,7 +279,7 @@
                         updateAccessibilityViewLocation(sensorBounds)
                     },
                     statusBarStateController,
-                    primaryBouncerInteractor,
+                    shadeInteractor,
                     dialogManager,
                     dumpManager
                 )
@@ -303,6 +305,7 @@
                     udfpsKeyguardAccessibilityDelegate,
                     selectedUserInteractor,
                     transitionInteractor,
+                    shadeInteractor,
                 )
             }
             REASON_AUTH_BP -> {
@@ -310,7 +313,7 @@
                 UdfpsBpViewController(
                     view.addUdfpsView(R.layout.udfps_bp_view),
                     statusBarStateController,
-                    primaryBouncerInteractor,
+                    shadeInteractor,
                     dialogManager,
                     dumpManager
                 )
@@ -320,7 +323,7 @@
                 UdfpsFpmEmptyViewController(
                     view.addUdfpsView(R.layout.udfps_fpm_empty_view),
                     statusBarStateController,
-                    primaryBouncerInteractor,
+                    shadeInteractor,
                     dialogManager,
                     dumpManager
                 )
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
index 88002e7..ab3fbb1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
@@ -15,9 +15,9 @@
  */
 package com.android.systemui.biometrics
 
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
 
 /**
@@ -28,13 +28,13 @@
 class UdfpsFpmEmptyViewController(
     view: UdfpsFpmEmptyView,
     statusBarStateController: StatusBarStateController,
-    primaryBouncerInteractor: PrimaryBouncerInteractor,
+    shadeInteractor: ShadeInteractor,
     systemUIDialogManager: SystemUIDialogManager,
     dumpManager: DumpManager
 ) : UdfpsAnimationViewController<UdfpsFpmEmptyView>(
     view,
     statusBarStateController,
-    primaryBouncerInteractor,
+    shadeInteractor,
     systemUIDialogManager,
     dumpManager
 ) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 64148f6..9f17024 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -68,16 +69,17 @@
     systemUIDialogManager: SystemUIDialogManager,
     private val udfpsController: UdfpsController,
     private val activityLaunchAnimator: ActivityLaunchAnimator,
-    primaryBouncerInteractor: PrimaryBouncerInteractor,
+    private val primaryBouncerInteractor: PrimaryBouncerInteractor,
     private val alternateBouncerInteractor: AlternateBouncerInteractor,
     private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
     private val selectedUserInteractor: SelectedUserInteractor,
     private val transitionInteractor: KeyguardTransitionInteractor,
+    shadeInteractor: ShadeInteractor,
 ) :
     UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>(
         view,
         statusBarStateController,
-        primaryBouncerInteractor,
+        shadeInteractor,
         systemUIDialogManager,
         dumpManager,
     ) {
@@ -319,7 +321,7 @@
     }
 
     @VisibleForTesting
-    override suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
+    suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
         return scope.launch {
             primaryBouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float ->
                 inputBouncerExpansion = bouncerExpansion
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 90e4a38..a7fb6f7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -97,7 +97,13 @@
 
         val iconOverlayView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon_overlay)
         val iconView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon)
-
+        /**
+         * View is only set visible in BiometricViewSizeBinder once PromptSize is determined that
+         * accounts for iconView size, to prevent prompt resizing being visible to the user.
+         *
+         * TODO(b/288175072): May be able to remove this once constraint layout is implemented
+         */
+        iconView.addLottieOnCompositionLoadedListener { viewModel.setIsIconViewLoaded(true) }
         PromptIconViewBinder.bind(
             iconView,
             iconOverlayView,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index 7e16d1e..f340bd8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -30,7 +30,6 @@
 import androidx.core.view.doOnLayout
 import androidx.core.view.isGone
 import androidx.lifecycle.lifecycleScope
-import com.android.systemui.res.R
 import com.android.systemui.biometrics.AuthPanelController
 import com.android.systemui.biometrics.Utils
 import com.android.systemui.biometrics.ui.BiometricPromptLayout
@@ -41,6 +40,8 @@
 import com.android.systemui.biometrics.ui.viewmodel.isNullOrNotSmall
 import com.android.systemui.biometrics.ui.viewmodel.isSmall
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
 /** Helper for [BiometricViewBinder] to handle resize transitions. */
@@ -92,8 +93,22 @@
             // TODO(b/251476085): migrate the legacy panel controller and simplify this
             view.repeatWhenAttached {
                 var currentSize: PromptSize? = null
+
                 lifecycleScope.launch {
-                    viewModel.size.collect { size ->
+                    /**
+                     * View is only set visible in BiometricViewSizeBinder once PromptSize is
+                     * determined that accounts for iconView size, to prevent prompt resizing being
+                     * visible to the user.
+                     *
+                     * TODO(b/288175072): May be able to remove isIconViewLoaded once constraint
+                     *   layout is implemented
+                     */
+                    combine(viewModel.isIconViewLoaded, viewModel.size, ::Pair).collect {
+                        (isIconViewLoaded, size) ->
+                        if (!isIconViewLoaded) {
+                            return@collect
+                        }
+
                         // prepare for animated size transitions
                         for (v in viewsToHideWhenSmall) {
                             v.showTextOrHide(forceHide = size.isSmall)
@@ -196,8 +211,9 @@
                                     }
                                 }
                             }
-
                             currentSize = size
+                            view.visibility = View.VISIBLE
+                            viewModel.setIsIconViewLoaded(false)
                             notifyAccessibilityChanged()
                         }
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 6d0a58e..d899827e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -192,6 +192,28 @@
     val iconViewModel: PromptIconViewModel =
         PromptIconViewModel(this, displayStateInteractor, promptSelectorInteractor)
 
+    private val _isIconViewLoaded = MutableStateFlow(false)
+
+    /**
+     * For prompts with an iconView, false until the prompt's iconView animation has been loaded in
+     * the view, otherwise true by default. Used for BiometricViewSizeBinder to wait for the icon
+     * asset to be loaded before determining the prompt size.
+     */
+    val isIconViewLoaded: Flow<Boolean> =
+        combine(credentialKind, _isIconViewLoaded.asStateFlow()) { credentialKind, isIconViewLoaded
+            ->
+            if (credentialKind is PromptKind.Biometric) {
+                isIconViewLoaded
+            } else {
+                true
+            }
+        }
+
+    // Sets whether the prompt's iconView animation has been loaded in the view yet.
+    fun setIsIconViewLoaded(iconViewLoaded: Boolean) {
+        _isIconViewLoaded.value = iconViewLoaded
+    }
+
     /** Padding for prompt UI elements */
     val promptPadding: Flow<Rect> =
         combine(size, displayStateInteractor.currentRotation) { size, rotation ->
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
index a12db6f..779446d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
@@ -117,10 +117,10 @@
     fun updateItemRank(itemUid: Long, order: Int)
 
     @Transaction
-    fun updateWidgetOrder(ids: List<Int>) {
-        ids.forEachIndexed { index, it ->
-            val widget = getWidgetByIdNow(it)
-            updateItemRank(widget.itemId, ids.size - index)
+    fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) {
+        widgetIdToPriorityMap.forEach { (id, priority) ->
+            val widget = getWidgetByIdNow(id)
+            updateItemRank(widget.itemId, priority)
         }
     }
 
@@ -129,7 +129,7 @@
         return insertWidget(
             widgetId = widgetId,
             componentName = provider.flattenToString(),
-            insertItemRank(priority),
+            itemId = insertItemRank(priority),
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index ded5581..d1bbe57 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -62,8 +62,12 @@
     /** Delete a widget by id from app widget service and the database. */
     fun deleteWidget(widgetId: Int) {}
 
-    /** Update the order of widgets in the database. */
-    fun updateWidgetOrder(ids: List<Int>) {}
+    /**
+     * Update the order of widgets in the database.
+     *
+     * @param widgetIdToPriorityMap mapping of the widget ids to the priority of the widget.
+     */
+    fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) {}
 }
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -168,11 +172,11 @@
         }
     }
 
-    override fun updateWidgetOrder(ids: List<Int>) {
+    override fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) {
         applicationScope.launch(bgDispatcher) {
-            communalWidgetDao.updateWidgetOrder(ids)
+            communalWidgetDao.updateWidgetOrder(widgetIdToPriorityMap)
             logger.i({ "Updated the order of widget list with ids: $str1." }) {
-                str1 = ids.toString()
+                str1 = widgetIdToPriorityMap.toString()
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index e342c6b..0f4e583 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -102,8 +102,13 @@
     /** Delete a widget by id. */
     fun deleteWidget(id: Int) = widgetRepository.deleteWidget(id)
 
-    /** Reorder widgets. The order in the list will be their display order in the hub. */
-    fun updateWidgetOrder(ids: List<Int>) = widgetRepository.updateWidgetOrder(ids)
+    /**
+     * Reorder the widgets.
+     *
+     * @param widgetIdToPriorityMap mapping of the widget ids to their new priorities.
+     */
+    fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) =
+        widgetRepository.updateWidgetOrder(widgetIdToPriorityMap)
 
     /** A list of widget content to be displayed in the communal hub. */
     val widgetContent: Flow<List<CommunalContentModel.Widget>> =
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
index bb9b4b5..3ae5229 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
@@ -20,6 +20,7 @@
 import android.appwidget.AppWidgetProviderInfo
 import android.widget.RemoteViews
 import com.android.systemui.communal.shared.model.CommunalContentSize
+import java.util.UUID
 
 /** Encapsulates data for a communal content. */
 sealed interface CommunalContentModel {
@@ -39,6 +40,13 @@
         override val size = CommunalContentSize.HALF
     }
 
+    /** A placeholder item representing a new widget being added */
+    class WidgetPlaceholder : CommunalContentModel {
+        override val key: String = "widget_placeholder_${UUID.randomUUID()}"
+        // Same as widget size.
+        override val size = CommunalContentSize.HALF
+    }
+
     class Tutorial(
         id: Int,
         override val size: CommunalContentSize,
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 708f137..577e404 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
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal.ui.viewmodel
 
+import android.content.ComponentName
 import android.os.PowerManager
 import android.os.SystemClock
 import android.view.MotionEvent
@@ -53,6 +54,13 @@
         communalInteractor.setTransitionState(transitionState)
     }
 
+    /**
+     * Called when a widget is added via drag and drop from the widget picker into the communal hub.
+     */
+    fun onAddWidget(componentName: ComponentName, priority: Int) {
+        communalInteractor.addWidget(componentName, priority)
+    }
+
     // TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't block
     //  touches anymore.
     /** Called when a touch is received outside the edge swipe area when hub mode is closed. */
@@ -82,8 +90,14 @@
     /** Called as the UI requests deleting a widget. */
     open fun onDeleteWidget(id: Int) {}
 
-    /** Called as the UI requests reordering widgets. */
-    open fun onReorderWidgets(ids: List<Int>) {}
+    /**
+     * Called as the UI requests reordering widgets.
+     *
+     * @param widgetIdToPriorityMap mapping of the widget ids to its priority. When re-ordering to
+     *   add a new item in the middle, provide the priorities of existing widgets as if the new item
+     *   existed, and then, call [onAddWidget] to add the new item at intended order.
+     */
+    open fun onReorderWidgets(widgetIdToPriorityMap: Map<Int, Int>) {}
 
     /** Called as the UI requests opening the widget editor. */
     open fun onOpenWidgetEditor() {}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index c82e000..368df9e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -47,5 +47,6 @@
 
     override fun onDeleteWidget(id: Int) = communalInteractor.deleteWidget(id)
 
-    override fun onReorderWidgets(ids: List<Int>) = communalInteractor.updateWidgetOrder(ids)
+    override fun onReorderWidgets(widgetIdToPriorityMap: Map<Int, Int>) =
+        communalInteractor.updateWidgetOrder(widgetIdToPriorityMap)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 0a13e48..c936c63 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -40,6 +40,7 @@
     private var windowManagerService: IWindowManager? = null,
 ) : ComponentActivity() {
     companion object {
+        private const val EXTRA_IS_PENDING_WIDGET_DRAG = "is_pending_widget_drag"
         private const val EXTRA_FILTER_STRATEGY = "filter_strategy"
         private const val FILTER_STRATEGY_GLANCEABLE_HUB = 1
         private const val TAG = "EditWidgetsActivity"
@@ -49,10 +50,23 @@
         registerForActivityResult(StartActivityForResult()) { result ->
             when (result.resultCode) {
                 RESULT_OK -> {
-                    result.data
-                        ?.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName::class.java)
-                        ?.let { communalInteractor.addWidget(it, 0) }
-                        ?: run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
+                    result.data?.let { intent ->
+                        val isPendingWidgetDrag =
+                            intent.getBooleanExtra(EXTRA_IS_PENDING_WIDGET_DRAG, false)
+                        // Nothing to do when a widget is being dragged & dropped. The drop
+                        // target in the communal grid will receive the widget to be added (if
+                        // the user drops it over).
+                        if (!isPendingWidgetDrag) {
+                            intent
+                                .getParcelableExtra(
+                                    Intent.EXTRA_COMPONENT_NAME,
+                                    ComponentName::class.java
+                                )
+                                ?.let { communalInteractor.addWidget(it, 0) }
+                                ?: run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
+                        }
+                    }
+                        ?: run { Log.w(TAG, "No data in result.") }
                 }
                 else ->
                     Log.w(
@@ -65,8 +79,6 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
-        setShowWhenLocked(true)
-
         setCommunalEditWidgetActivityContent(
             activity = this,
             viewModel = communalViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index f6db978..1b35005 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -585,10 +585,6 @@
     @JvmField
     val SPLIT_SHADE_SUBPIXEL_OPTIMIZATION = unreleasedFlag("split_shade_subpixel_optimization")
 
-    // TODO(b/288868056): Tracking Bug
-    @JvmField
-    val PARTIAL_SCREEN_SHARING_TASK_SWITCHER = unreleasedFlag("pss_task_switcher")
-
     // TODO(b/278761837): Tracking Bug
     @JvmField val USE_NEW_ACTIVITY_STARTER = releasedFlag(name = "use_new_activity_starter")
 
@@ -609,11 +605,6 @@
     val BIGPICTURE_NOTIFICATION_LAZY_LOADING =
         unreleasedFlag("bigpicture_notification_lazy_loading")
 
-    // TODO(b/292062937): Tracking bug
-    @JvmField
-    val NOTIFICATION_CLEARABLE_REFACTOR =
-            unreleasedFlag("notification_clearable_refactor")
-
     // TODO(b/283740863): Tracking Bug
     @JvmField
     val ENABLE_NEW_PRIVACY_DIALOG = releasedFlag("enable_new_privacy_dialog")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index c490ce7..342325f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -30,7 +30,7 @@
 import android.os.Binder
 import android.os.Bundle
 import android.util.Log
-import com.android.app.tracing.TraceUtils.Companion.runBlocking
+import com.android.app.tracing.coroutines.runBlocking
 import com.android.systemui.SystemUIAppComponentFactoryBase
 import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
 import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 9fe5c3f..cecc653 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -18,7 +18,7 @@
 
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
-import com.android.app.tracing.TraceUtils.Companion.launch
+import com.android.app.tracing.coroutines.launch
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.flags.FeatureFlags
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 7882a97..3888345 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -22,7 +22,7 @@
 import android.content.Context
 import android.content.Intent
 import android.util.Log
-import com.android.app.tracing.TraceUtils.Companion.withContext
+import com.android.app.tracing.coroutines.withContext
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.animation.Expandable
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index 64ff3b0c..1277585 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -32,11 +32,8 @@
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharedFlow
-import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
 
 /**
  * Assists in creating sub-flows for a KeyguardTransition. Call [setup] once for a transition, and
@@ -68,8 +65,6 @@
          * in the range of [0, 1]. View animations should begin and end within a subset of this
          * range. This function maps the [startTime] and [duration] into [0, 1], when this subset is
          * valid.
-         *
-         * Will produce a [SharedFlow], so that identical animations can use the same value.
          */
         fun sharedFlow(
             duration: Duration,
@@ -80,7 +75,7 @@
             onFinish: (() -> Float)? = null,
             interpolator: Interpolator = LINEAR,
             name: String? = null
-        ): SharedFlow<Float> {
+        ): Flow<Float> {
             if (!duration.isPositive()) {
                 throw IllegalArgumentException("duration must be a positive number: $duration")
             }
@@ -137,7 +132,6 @@
                     value
                 }
                 .filterNotNull()
-                .shareIn(scope, SharingStarted.WhileSubscribed())
         }
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
index f4ae365..fa4de04 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
@@ -20,12 +20,12 @@
 import android.hardware.biometrics.SensorLocationInternal
 import com.android.settingslib.Utils
 import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
 import com.android.systemui.res.R
 import javax.inject.Inject
-import kotlin.math.roundToInt
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
@@ -45,6 +45,7 @@
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
     deviceEntryBackgroundViewModel: DeviceEntryBackgroundViewModel,
     fingerprintPropertyRepository: FingerprintPropertyRepository,
+    udfpsOverlayInteractor: UdfpsOverlayInteractor,
 ) {
     private val isSupported: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported
     val iconLocation: Flow<IconLocation> =
@@ -73,11 +74,7 @@
             .onStart {
                 emit(Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary))
             }
-    private val fgIconPadding: Flow<Int> =
-        configurationInteractor.scaleForResolution.map { scale ->
-            (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
-                .roundToInt()
-        }
+    private val fgIconPadding: Flow<Int> = udfpsOverlayInteractor.iconPadding
     val fgViewModel: Flow<DeviceEntryForegroundViewModel.ForegroundIconViewModel> =
         combine(
             fgIconColor,
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index d8bb3e6..0d5ba64 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -26,6 +26,7 @@
 import com.android.systemui.log.echo.LogcatEchoTrackerProd;
 import com.android.systemui.log.table.TableLogBuffer;
 import com.android.systemui.log.table.TableLogBufferFactory;
+import com.android.systemui.plugins.clocks.ClockMessageBuffers;
 import com.android.systemui.qs.QSFragmentLegacy;
 import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
 import com.android.systemui.qs.pipeline.shared.TileSpec;
@@ -417,6 +418,18 @@
     }
 
     /**
+     * Provides a {@link ClockMessageBuffers} which contains the keyguard clock message buffers.
+     */
+    @Provides
+    public static ClockMessageBuffers provideKeyguardClockMessageBuffers(
+            @KeyguardClockLog LogBuffer infraClockLog,
+            @KeyguardSmallClockLog LogBuffer smallClockLog,
+            @KeyguardLargeClockLog LogBuffer largeClockLog
+    ) {
+        return new ClockMessageBuffers(infraClockLog, smallClockLog, largeClockLog);
+    }
+
+    /**
      * Provides a {@link LogBuffer} for use by {@link com.android.keyguard.KeyguardUpdateMonitor}.
      */
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
index 724241d..185a783 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.media.controls.pipeline
 
 import android.content.Context
+import android.content.pm.UserInfo
 import android.os.SystemProperties
 import android.util.Log
 import com.android.internal.annotations.KeepForWeakReference
@@ -88,7 +89,11 @@
     private val userTrackerCallback =
         object : UserTracker.Callback {
             override fun onUserChanged(newUser: Int, userContext: Context) {
-                handleUserSwitched(newUser)
+                handleUserSwitched()
+            }
+
+            override fun onProfilesChanged(profiles: List<UserInfo>) {
+                handleProfileChanged()
             }
         }
 
@@ -109,7 +114,10 @@
         }
         allEntries.put(key, data)
 
-        if (!lockscreenUserManager.isCurrentProfile(data.userId)) {
+        if (
+            !lockscreenUserManager.isCurrentProfile(data.userId) ||
+                !lockscreenUserManager.isProfileAvailable(data.userId)
+        ) {
             return
         }
 
@@ -231,7 +239,20 @@
     }
 
     @VisibleForTesting
-    internal fun handleUserSwitched(id: Int) {
+    internal fun handleProfileChanged() {
+        // TODO(b/317221348) re-add media removed when profile is available.
+        allEntries.forEach { (key, data) ->
+            if (!lockscreenUserManager.isProfileAvailable(data.userId)) {
+                // Only remove media when the profile is unavailable.
+                if (DEBUG) Log.d(TAG, "Removing $key after profile change")
+                userEntries.remove(key, data)
+                listeners.forEach { listener -> listener.onMediaDataRemoved(key) }
+            }
+        }
+    }
+
+    @VisibleForTesting
+    internal fun handleUserSwitched() {
         // If the user changes, remove all current MediaData objects and inform listeners
         val listenersCopy = listeners
         val keyCopy = userEntries.keys.toMutableList()
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartable.kt
index 3c50127..2408af1 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartable.kt
@@ -17,23 +17,22 @@
 package com.android.systemui.mediaprojection.taskswitcher
 
 import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.pssTaskSwitcher
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.mediaprojection.taskswitcher.ui.TaskSwitcherNotificationCoordinator
+import dagger.Lazy
 import javax.inject.Inject
 
 @SysUISingleton
 class MediaProjectionTaskSwitcherCoreStartable
 @Inject
 constructor(
-    private val notificationCoordinator: TaskSwitcherNotificationCoordinator,
-    private val featureFlags: FeatureFlags,
+    private val notificationCoordinatorLazy: Lazy<TaskSwitcherNotificationCoordinator>,
 ) : CoreStartable {
 
     override fun start() {
-        if (featureFlags.isEnabled(Flags.PARTIAL_SCREEN_SHARING_TASK_SWITCHER)) {
-            notificationCoordinator.start()
+        if (pssTaskSwitcher()) {
+            notificationCoordinatorLazy.get().start()
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 270bfbe..3aa9daa 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -37,7 +37,7 @@
 import android.provider.Settings
 import android.widget.Toast
 import androidx.annotation.VisibleForTesting
-import com.android.app.tracing.TraceUtils.Companion.launch
+import com.android.app.tracing.coroutines.launch
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
index 2345667..83b6f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
@@ -19,16 +19,21 @@
 import android.service.quicksettings.IQSTileService;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 
 public class QSTileServiceWrapper {
     private static final String TAG = "IQSTileServiceWrapper";
 
+    @NonNull
     private final IQSTileService mService;
 
-    public QSTileServiceWrapper(IQSTileService service) {
+    public QSTileServiceWrapper(@NonNull IQSTileService service) {
         mService = service;
     }
 
+    // This can never be null, as it's the constructor parameter and it's final
+    @NonNull
     public IBinder asBinder() {
         return mService.asBinder();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index e08eb37..880289e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -40,6 +40,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
 
@@ -54,8 +55,10 @@
 
 import java.util.NoSuchElementException;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Predicate;
 
 /**
  * Manages the lifecycle of a TileService.
@@ -101,8 +104,8 @@
     private final ActivityManager mActivityManager;
 
     private Set<Integer> mQueuedMessages = new ArraySet<>();
-    @Nullable
-    private volatile QSTileServiceWrapper mWrapper;
+    @NonNull
+    private volatile Optional<QSTileServiceWrapper> mOptionalWrapper = Optional.empty();
     private boolean mListening;
     private IBinder mClickBinder;
 
@@ -222,6 +225,7 @@
                 // Only try a new binding if we are not currently bound.
                 mIsBound.compareAndSet(false, bindServices());
                 if (!mIsBound.get()) {
+                    Log.d(TAG, "Failed to bind to service");
                     mContext.unbindService(this);
                 }
             } catch (SecurityException e) {
@@ -281,7 +285,7 @@
             service.linkToDeath(this, 0);
         } catch (RemoteException e) {
         }
-        mWrapper = wrapper;
+        mOptionalWrapper = Optional.of(wrapper);
         handlePendingMessages();
     }
 
@@ -368,6 +372,10 @@
      * are supposed to be bound, we will try to bind after some amount of time.
      */
     private void handleDeath() {
+        if (!mIsBound.get()) {
+            // If we are already not bound, don't do anything else.
+            return;
+        }
         mExecutor.execute(() -> {
             if (!mIsBound.get()) {
                 // If we are already not bound, don't do anything else.
@@ -522,7 +530,7 @@
     @Override
     public void onTileAdded() {
         if (mDebug) Log.d(TAG, "onTileAdded " + getComponent());
-        if (mWrapper == null || !mWrapper.onTileAdded()) {
+        if (isNullOrFailedAction(mOptionalWrapper, QSTileServiceWrapper::onTileAdded)) {
             queueMessage(MSG_ON_ADDED);
             handleDeath();
         }
@@ -531,7 +539,7 @@
     @Override
     public void onTileRemoved() {
         if (mDebug) Log.d(TAG, "onTileRemoved " + getComponent());
-        if (mWrapper == null || !mWrapper.onTileRemoved()) {
+        if (isNullOrFailedAction(mOptionalWrapper, QSTileServiceWrapper::onTileRemoved)) {
             queueMessage(MSG_ON_REMOVED);
             handleDeath();
         }
@@ -541,7 +549,7 @@
     public void onStartListening() {
         if (mDebug) Log.d(TAG, "onStartListening " + getComponent());
         mListening = true;
-        if (mWrapper != null && !mWrapper.onStartListening()) {
+        if (isNotNullAndFailedAction(mOptionalWrapper, QSTileServiceWrapper::onStartListening)) {
             handleDeath();
         }
     }
@@ -550,7 +558,7 @@
     public void onStopListening() {
         if (mDebug) Log.d(TAG, "onStopListening " + getComponent());
         mListening = false;
-        if (mWrapper != null && !mWrapper.onStopListening()) {
+        if (isNotNullAndFailedAction(mOptionalWrapper, QSTileServiceWrapper::onStopListening)) {
             handleDeath();
         }
     }
@@ -558,7 +566,7 @@
     @Override
     public void onClick(IBinder iBinder) {
         if (mDebug) Log.d(TAG, "onClick " + iBinder + " " + getComponent() + " " + mUser);
-        if (mWrapper == null || !mWrapper.onClick(iBinder)) {
+        if (isNullOrFailedAction(mOptionalWrapper, (wrapper) -> wrapper.onClick(iBinder))) {
             mClickBinder = iBinder;
             queueMessage(MSG_ON_CLICK);
             handleDeath();
@@ -568,7 +576,7 @@
     @Override
     public void onUnlockComplete() {
         if (mDebug) Log.d(TAG, "onUnlockComplete " + getComponent());
-        if (mWrapper == null || !mWrapper.onUnlockComplete()) {
+        if (isNullOrFailedAction(mOptionalWrapper, QSTileServiceWrapper::onUnlockComplete)) {
             queueMessage(MSG_ON_UNLOCK_COMPLETE);
             handleDeath();
         }
@@ -577,7 +585,7 @@
     @Nullable
     @Override
     public IBinder asBinder() {
-        return mWrapper != null ? mWrapper.asBinder() : null;
+        return mOptionalWrapper.map(QSTileServiceWrapper::asBinder).orElse(null);
     }
 
     @Override
@@ -591,18 +599,42 @@
     }
 
     private void freeWrapper() {
-        if (mWrapper != null) {
+        if (mOptionalWrapper.isPresent()) {
             try {
-                mWrapper.asBinder().unlinkToDeath(this, 0);
+                mOptionalWrapper.ifPresent(
+                        (wrapper) -> wrapper.asBinder().unlinkToDeath(this, 0)
+                );
             } catch (NoSuchElementException e) {
                 Log.w(TAG, "Trying to unlink not linked recipient for component"
                         + mIntent.getComponent().flattenToShortString());
             }
-            mWrapper = null;
+            mOptionalWrapper = Optional.empty();
         }
     }
 
     public interface TileChangeListener {
         void onTileChanged(ComponentName tile);
     }
+
+    /**
+     * Returns true if the Optional is empty OR performing the action on the content of the Optional
+     * (when not empty) fails.
+     */
+    private static boolean isNullOrFailedAction(
+            Optional<QSTileServiceWrapper> optionalWrapper,
+            Predicate<QSTileServiceWrapper> action
+    ) {
+        return !optionalWrapper.map(action::test).orElse(false);
+    }
+
+    /**
+     * Returns true if the Optional is not empty AND performing the action on the content of
+     * the Optional fails.
+     */
+    private static boolean isNotNullAndFailedAction(
+            Optional<QSTileServiceWrapper> optionalWrapper,
+            Predicate<QSTileServiceWrapper> action
+    ) {
+        return  !optionalWrapper.map(action::test).orElse(true);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index 66da8bd..216d716 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -42,9 +42,7 @@
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.recordissue.RecordIssueDialogDelegate
 import com.android.systemui.res.R
-import com.android.systemui.settings.UserContextProvider
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
-import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import javax.inject.Inject
 
@@ -63,8 +61,7 @@
     private val keyguardDismissUtil: KeyguardDismissUtil,
     private val keyguardStateController: KeyguardStateController,
     private val dialogLaunchAnimator: DialogLaunchAnimator,
-    private val sysuiDialogFactory: SystemUIDialog.Factory,
-    private val userContextProvider: UserContextProvider,
+    private val delegateFactory: RecordIssueDialogDelegate.Factory,
 ) :
     QSTileImpl<QSTile.BooleanState>(
         host,
@@ -102,7 +99,8 @@
 
     private fun showPrompt(view: View?) {
         val dialog: AlertDialog =
-            RecordIssueDialogDelegate(sysuiDialogFactory, userContextProvider) {
+            delegateFactory
+                .create {
                     isRecording = true
                     refreshState()
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
index 5bf44fa..e051df4 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -24,27 +24,63 @@
 import android.content.res.ColorStateList
 import android.graphics.Color
 import android.os.Bundle
+import android.os.UserHandle
 import android.view.Gravity
 import android.view.LayoutInflater
 import android.view.WindowManager
 import android.widget.Button
 import android.widget.PopupMenu
 import android.widget.Switch
+import androidx.annotation.AnyThread
+import androidx.annotation.MainThread
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.mediaprojection.SessionCreationSource
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog
+import com.android.systemui.qs.tiles.RecordIssueTile
 import com.android.systemui.res.R
 import com.android.systemui.screenrecord.RecordingService
 import com.android.systemui.screenrecord.ScreenRecordingAudioSource
 import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.concurrent.Executor
 
-class RecordIssueDialogDelegate(
+class RecordIssueDialogDelegate
+@AssistedInject
+constructor(
     private val factory: SystemUIDialog.Factory,
     private val userContextProvider: UserContextProvider,
-    private val onStarted: Runnable
+    private val userTracker: UserTracker,
+    private val flags: FeatureFlagsClassic,
+    @Background private val bgExecutor: Executor,
+    @Main private val mainExecutor: Executor,
+    private val devicePolicyResolver: dagger.Lazy<ScreenCaptureDevicePolicyResolver>,
+    private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
+    private val userFileManager: UserFileManager,
+    @Assisted private val onStarted: Runnable,
 ) : SystemUIDialog.Delegate {
 
+    /** To inject dependencies and allow for easier testing */
+    @AssistedFactory
+    interface Factory {
+        /** Create a dialog object */
+        fun create(onStarted: Runnable): RecordIssueDialogDelegate
+    }
+
     @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch
     private lateinit var issueTypeButton: Button
 
+    @MainThread
     override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
         dialog.apply {
             setView(LayoutInflater.from(context).inflate(R.layout.record_issue_dialog, null))
@@ -63,17 +99,64 @@
 
     override fun createDialog(): SystemUIDialog = factory.create(this)
 
+    @MainThread
     override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
         dialog.apply {
             window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
             window?.setGravity(Gravity.CENTER)
 
             screenRecordSwitch = requireViewById(R.id.screenrecord_switch)
+            screenRecordSwitch.setOnCheckedChangeListener { _, isEnabled ->
+                onScreenRecordSwitchClicked(context, isEnabled)
+            }
             issueTypeButton = requireViewById(R.id.issue_type_button)
             issueTypeButton.setOnClickListener { onIssueTypeClicked(context) }
         }
     }
 
+    @AnyThread
+    private fun onScreenRecordSwitchClicked(context: Context, isEnabled: Boolean) {
+        if (!isEnabled) return
+
+        bgExecutor.execute {
+            if (
+                flags.isEnabled(WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES) &&
+                    devicePolicyResolver
+                        .get()
+                        .isScreenCaptureCompletelyDisabled(UserHandle.of(userTracker.userId))
+            ) {
+                mainExecutor.execute {
+                    ScreenCaptureDisabledDialog(context).show()
+                    screenRecordSwitch.isChecked = false
+                }
+                return@execute
+            }
+
+            mediaProjectionMetricsLogger.notifyProjectionInitiated(
+                userTracker.userId,
+                SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER
+            )
+
+            if (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)) {
+                val prefs =
+                    userFileManager.getSharedPreferences(
+                        RecordIssueTile.TILE_SPEC,
+                        Context.MODE_PRIVATE,
+                        userTracker.userId
+                    )
+                if (!prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false)) {
+                    mainExecutor.execute {
+                        ScreenCapturePermissionDialogDelegate(factory, prefs).createDialog().apply {
+                            setOnCancelListener { screenRecordSwitch.isChecked = false }
+                            show()
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @MainThread
     private fun onIssueTypeClicked(context: Context) {
         val selectedCategory = issueTypeButton.text.toString()
         val popupMenu = PopupMenu(context, issueTypeButton)
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt
new file mode 100644
index 0000000..de6d3f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.recordissue
+
+import android.content.SharedPreferences
+import android.os.Bundle
+import android.view.Gravity
+import android.view.WindowManager
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord"
+
+class ScreenCapturePermissionDialogDelegate(
+    private val dialogFactory: SystemUIDialog.Factory,
+    private val sharedPreferences: SharedPreferences,
+) : SystemUIDialog.Delegate {
+
+    override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+        dialog.apply {
+            setIcon(R.drawable.ic_screenrecord)
+            setTitle(R.string.screenrecord_permission_dialog_title)
+            setMessage(R.string.screenrecord_permission_dialog_warning_entire_screen)
+            setNegativeButton(R.string.slice_permission_deny) { _, _ -> cancel() }
+            setPositiveButton(R.string.slice_permission_allow) { _, _ ->
+                sharedPreferences.edit().putBoolean(HAS_APPROVED_SCREEN_RECORDING, true).apply()
+                dismiss()
+            }
+            window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
+            window?.setGravity(Gravity.CENTER)
+        }
+    }
+
+    override fun createDialog(): SystemUIDialog = dialogFactory.create(this)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index a950539..bee3152 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -29,7 +29,7 @@
 import android.view.RemoteAnimationTarget
 import android.view.WindowManager
 import android.view.WindowManagerGlobal
-import com.android.app.tracing.TraceUtils.Companion.launch
+import com.android.app.tracing.coroutines.launch
 import com.android.internal.infra.ServiceConnector
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
index f56f416..3081f89 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -20,7 +20,7 @@
 import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.app.tracing.TraceUtils.Companion.launch
+import com.android.app.tracing.coroutines.launch
 import kotlinx.coroutines.CoroutineScope
 import java.util.function.Consumer
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
index 713ede6..86f6523 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.shade.ShadeExpansionStateManager
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
-import com.android.app.tracing.TraceUtils.Companion.launch
+import com.android.app.tracing.coroutines.launch
 import kotlinx.coroutines.withContext
 
 /** Provides state from the main SystemUI process on behalf of the Screenshot process. */
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
index 38d00f7..238a552 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
@@ -18,7 +18,7 @@
 
 import android.media.MediaPlayer
 import android.util.Log
-import com.android.app.tracing.TraceUtils.Companion.async
+import com.android.app.tracing.coroutines.async
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.google.errorprone.annotations.CanIgnoreReturnValue
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index f6c25e0..e464fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -5,7 +5,7 @@
 import android.util.Log
 import android.view.Display
 import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-import com.android.app.tracing.TraceUtils.Companion.launch
+import com.android.app.tracing.coroutines.launch
 import com.android.internal.logging.UiEventLogger
 import com.android.internal.util.ScreenshotRequest
 import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 9f416bb..f2fa0ef 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -135,6 +135,10 @@
         val filter = IntentFilter().apply {
             addAction(Intent.ACTION_LOCALE_CHANGED)
             addAction(Intent.ACTION_USER_INFO_CHANGED)
+            addAction(Intent.ACTION_PROFILE_ADDED)
+            addAction(Intent.ACTION_PROFILE_REMOVED)
+            addAction(Intent.ACTION_PROFILE_AVAILABLE)
+            addAction(Intent.ACTION_PROFILE_UNAVAILABLE)
             // These get called when a managed profile goes in or out of quiet mode.
             addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
             addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
@@ -157,7 +161,11 @@
             Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
             Intent.ACTION_MANAGED_PROFILE_ADDED,
             Intent.ACTION_MANAGED_PROFILE_REMOVED,
-            Intent.ACTION_MANAGED_PROFILE_UNLOCKED -> {
+            Intent.ACTION_MANAGED_PROFILE_UNLOCKED,
+            Intent.ACTION_PROFILE_ADDED,
+            Intent.ACTION_PROFILE_REMOVED,
+            Intent.ACTION_PROFILE_AVAILABLE,
+            Intent.ACTION_PROFILE_UNAVAILABLE -> {
                 handleProfilesChanged()
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 93c55de..71efbab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -44,6 +44,13 @@
 
     boolean isCurrentProfile(int userId);
 
+    /**
+     *
+     * @param userId user Id
+     * @return true if user profile is running.
+     */
+    boolean isProfileAvailable(int userId);
+
     /** Adds a listener to be notified when the current user changes. */
     void addUserChangedListener(UserChangedListener listener);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 05c3839..633510d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -461,6 +461,13 @@
         }
     }
 
+    @SuppressLint("AndroidFrameworkRequiresPermission")
+    public boolean isProfileAvailable(int userId) {
+        synchronized (mLock) {
+            return mUserManager.isUserRunning(userId);
+        }
+    }
+
     private void setShowLockscreenNotifications(boolean show) {
         mShowLockscreenNotifications = show;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 2740cc6..11456ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -453,11 +453,11 @@
     public void initNotificationIconArea() {
         ViewGroup notificationIconArea = mStatusBar.requireViewById(R.id.notification_icon_area);
         if (NotificationIconContainerRefactor.isEnabled()) {
-            mNotificationIconAreaInner =
-                LayoutInflater.from(getContext())
-                        .inflate(R.layout.notification_icon_area, notificationIconArea, true);
+            LayoutInflater.from(getContext())
+                    .inflate(R.layout.notification_icon_area, notificationIconArea, true);
             NotificationIconContainer notificationIcons =
                     notificationIconArea.requireViewById(R.id.notificationIcons);
+            mNotificationIconAreaInner = notificationIcons;
             mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
         } else {
             mNotificationIconAreaInner =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index 425da5f..48bf7ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -27,6 +27,7 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -48,7 +49,7 @@
     override val subId: Int,
     startingIsCarrierMerged: Boolean,
     override val tableLogBuffer: TableLogBuffer,
-    subscriptionModel: StateFlow<SubscriptionModel?>,
+    subscriptionModel: Flow<SubscriptionModel?>,
     private val defaultNetworkName: NetworkNameModel,
     private val networkNameSeparator: String,
     @Application scope: CoroutineScope,
@@ -331,7 +332,7 @@
         fun build(
             subId: Int,
             startingIsCarrierMerged: Boolean,
-            subscriptionModel: StateFlow<SubscriptionModel?>,
+            subscriptionModel: Flow<SubscriptionModel?>,
             defaultNetworkName: NetworkNameModel,
             networkNameSeparator: String,
         ): FullMobileConnectionRepository {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 4fb99c24..be2c21b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -96,7 +96,7 @@
 class MobileConnectionRepositoryImpl(
     override val subId: Int,
     private val context: Context,
-    subscriptionModel: StateFlow<SubscriptionModel?>,
+    subscriptionModel: Flow<SubscriptionModel?>,
     defaultNetworkName: NetworkNameModel,
     networkNameSeparator: String,
     connectivityManager: ConnectivityManager,
@@ -448,7 +448,7 @@
         fun build(
             subId: Int,
             mobileLogger: TableLogBuffer,
-            subscriptionModel: StateFlow<SubscriptionModel?>,
+            subscriptionModel: Flow<SubscriptionModel?>,
             defaultNetworkName: NetworkNameModel,
             networkNameSeparator: String,
         ): MobileConnectionRepository {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 2a510e4..a455db2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -357,10 +357,10 @@
 
     @VisibleForTesting fun getSubIdRepoCache() = subIdRepositoryCache
 
-    private fun subscriptionModelForSubId(subId: Int): StateFlow<SubscriptionModel?> {
-        return subscriptions
-            .map { list -> list.firstOrNull { model -> model.subscriptionId == subId } }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+    private fun subscriptionModelForSubId(subId: Int): Flow<SubscriptionModel?> {
+        return subscriptions.map { list ->
+            list.firstOrNull { model -> model.subscriptionId == subId }
+        }
     }
 
     private fun createRepositoryForSubId(subId: Int): FullMobileConnectionRepository {
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
index 472f0ae..cf6b0d9 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
@@ -5,9 +5,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dagger.qualifiers.Tracing
-import com.android.systemui.Flags.coroutineTracing
-import com.android.app.tracing.TraceUtils.Companion.coroutineTracingIsEnabled
-import com.android.app.tracing.TraceContextElement
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import dagger.Module
 import dagger.Provides
 import kotlinx.coroutines.CoroutineDispatcher
@@ -16,7 +14,6 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.plus
 import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
 
 /** Providers for various coroutines-related constructs. */
 @Module
@@ -83,9 +80,6 @@
     @Tracing
     @SysUISingleton
     fun tracingCoroutineContext(): CoroutineContext {
-        return if (coroutineTracing()) {
-            coroutineTracingIsEnabled = true
-            TraceContextElement()
-        } else EmptyCoroutineContext
+        return createCoroutineTracingContext()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 6f58bc2..e6637e6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -30,13 +30,15 @@
 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.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogcatOnlyMessageBuffer
 import com.android.systemui.plugins.clocks.ClockAnimations
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockEvents
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceController
 import com.android.systemui.plugins.clocks.ClockFaceEvents
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockTickRate
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -94,9 +96,9 @@
     @Mock private lateinit var largeClockEvents: ClockFaceEvents
     @Mock private lateinit var parentView: View
     private lateinit var repository: FakeKeyguardRepository
-    @Mock private lateinit var smallLogBuffer: LogBuffer
-    @Mock private lateinit var largeLogBuffer: LogBuffer
     @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+    private val messageBuffer = LogcatOnlyMessageBuffer(LogLevel.DEBUG)
+    private val clockBuffers = ClockMessageBuffers(messageBuffer, messageBuffer, messageBuffer)
     private lateinit var underTest: ClockEventController
     @Mock private lateinit var zenModeController: ZenModeController
 
@@ -140,8 +142,7 @@
                 context,
                 mainExecutor,
                 bgExecutor,
-                smallLogBuffer,
-                largeLogBuffer,
+                clockBuffers,
                 withDeps.featureFlags,
                 zenModeController
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index f8856c9..bd49927 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -102,8 +102,9 @@
                 getContext().getMainThreadHandler(), mCommandQueue,
                 mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings,
                 mDisplayTracker, getContext().getSystemService(DisplayManager.class), mA11yLogger);
-        mMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier(
-                mContext.getSystemService(DisplayManager.class));
+        mMagnification.mWindowMagnificationControllerSupplier =
+                new FakeWindowMagnificationControllerSupplier(
+                        mContext.getSystemService(DisplayManager.class));
         mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
                 mContext.getSystemService(DisplayManager.class));
 
@@ -201,10 +202,10 @@
         verify(mMagnificationSettingsController).setMagnificationScale(eq(testScale));
     }
 
-    private class FakeControllerSupplier extends
+    private class FakeWindowMagnificationControllerSupplier extends
             DisplayIdIndexSupplier<WindowMagnificationController> {
 
-        FakeControllerSupplier(DisplayManager displayManager) {
+        FakeWindowMagnificationControllerSupplier(DisplayManager displayManager) {
             super(displayManager);
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
index d0e1678..3b5cbea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -124,7 +124,7 @@
                 getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController,
                 mSysUiState, mOverviewProxyService, mSecureSettings, mDisplayTracker,
                 getContext().getSystemService(DisplayManager.class), mA11yLogger);
-        mMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier(
+        mMagnification.mWindowMagnificationControllerSupplier = new FakeControllerSupplier(
                 mContext.getSystemService(DisplayManager.class), mWindowMagnificationController);
         mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
                 mContext.getSystemService(DisplayManager.class), mMagnificationSettingsController);
@@ -325,9 +325,9 @@
     @Test
     public void overviewProxyIsConnected_controllerIsAvailable_updateSysUiStateFlag() {
         final WindowMagnificationController mController = mock(WindowMagnificationController.class);
-        mMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier(
+        mMagnification.mWindowMagnificationControllerSupplier = new FakeControllerSupplier(
                 mContext.getSystemService(DisplayManager.class), mController);
-        mMagnification.mMagnificationControllerSupplier.get(TEST_DISPLAY);
+        mMagnification.mWindowMagnificationControllerSupplier.get(TEST_DISPLAY);
 
         mOverviewProxyListener.onConnectionChanged(true);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
index e2aa984..647dae6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
@@ -20,10 +20,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import org.junit.Assert.assertFalse
 import org.junit.Before
@@ -42,8 +41,7 @@
 
     @Mock lateinit var udfpsBpView: UdfpsBpView
     @Mock lateinit var statusBarStateController: StatusBarStateController
-    @Mock lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
-    @Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
+    @Mock lateinit var shadeInteractor: ShadeInteractor
     @Mock lateinit var systemUIDialogManager: SystemUIDialogManager
     @Mock lateinit var dumpManager: DumpManager
 
@@ -55,7 +53,7 @@
             UdfpsBpViewController(
                 udfpsBpView,
                 statusBarStateController,
-                primaryBouncerInteractor,
+                shadeInteractor,
                 systemUIDialogManager,
                 dumpManager
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
index 16b2ed6..9c5cd71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
@@ -140,21 +140,76 @@
             }
             assertThat(widgets())
                 .containsExactly(
+                    communalItemRankEntry2,
+                    communalWidgetItemEntry2,
                     communalItemRankEntry1,
                     communalWidgetItemEntry1,
-                    communalItemRankEntry2,
+                )
+                .inOrder()
+
+            // swapped priorities
+            val widgetIdsToPriorityMap = mapOf(widgetInfo1.widgetId to 2, widgetInfo2.widgetId to 1)
+            communalWidgetDao.updateWidgetOrder(widgetIdsToPriorityMap)
+            assertThat(widgets())
+                .containsExactly(
+                    communalItemRankEntry1.copy(rank = 2),
+                    communalWidgetItemEntry1,
+                    communalItemRankEntry2.copy(rank = 1),
                     communalWidgetItemEntry2
                 )
+                .inOrder()
+        }
 
-            val widgetIdsInNewOrder = listOf(widgetInfo2.widgetId, widgetInfo1.widgetId)
-            communalWidgetDao.updateWidgetOrder(widgetIdsInNewOrder)
+    @Test
+    fun addNewWidgetWithReorder_emitsWidgetsInNewOrder() =
+        testScope.runTest {
+            val existingWidgets = listOf(widgetInfo1, widgetInfo2)
+            val widgets = collectLastValue(communalWidgetDao.getWidgets())
+
+            existingWidgets.forEach {
+                val (widgetId, provider, priority) = it
+                communalWidgetDao.addWidget(
+                    widgetId = widgetId,
+                    provider = provider,
+                    priority = priority,
+                )
+            }
             assertThat(widgets())
                 .containsExactly(
                     communalItemRankEntry2,
                     communalWidgetItemEntry2,
                     communalItemRankEntry1,
-                    communalWidgetItemEntry1
+                    communalWidgetItemEntry1,
                 )
+                .inOrder()
+
+            // map with no item in the middle at index 1
+            val widgetIdsToIndexMap = mapOf(widgetInfo1.widgetId to 1, widgetInfo2.widgetId to 3)
+            communalWidgetDao.updateWidgetOrder(widgetIdsToIndexMap)
+            assertThat(widgets())
+                .containsExactly(
+                    communalItemRankEntry2.copy(rank = 3),
+                    communalWidgetItemEntry2,
+                    communalItemRankEntry1.copy(rank = 1),
+                    communalWidgetItemEntry1,
+                )
+                .inOrder()
+            // add the new middle item that we left space for.
+            communalWidgetDao.addWidget(
+                widgetId = widgetInfo3.widgetId,
+                provider = widgetInfo3.provider,
+                priority = 2,
+            )
+            assertThat(widgets())
+                .containsExactly(
+                    communalItemRankEntry2.copy(rank = 3),
+                    communalWidgetItemEntry2,
+                    communalItemRankEntry3.copy(rank = 2),
+                    communalWidgetItemEntry3,
+                    communalItemRankEntry1.copy(rank = 1),
+                    communalWidgetItemEntry1,
+                )
+                .inOrder()
         }
 
     data class FakeWidgetMetadata(
@@ -176,8 +231,15 @@
                 provider = ComponentName("pk_name", "cls_name_2"),
                 priority = 2
             )
+        val widgetInfo3 =
+            FakeWidgetMetadata(
+                widgetId = 3,
+                provider = ComponentName("pk_name", "cls_name_3"),
+                priority = 3
+            )
         val communalItemRankEntry1 = CommunalItemRank(uid = 1L, rank = widgetInfo1.priority)
         val communalItemRankEntry2 = CommunalItemRank(uid = 2L, rank = widgetInfo2.priority)
+        val communalItemRankEntry3 = CommunalItemRank(uid = 3L, rank = widgetInfo3.priority)
         val communalWidgetItemEntry1 =
             CommunalWidgetItem(
                 uid = 1L,
@@ -192,5 +254,12 @@
                 componentName = widgetInfo2.provider.flattenToString(),
                 itemId = communalItemRankEntry2.uid,
             )
+        val communalWidgetItemEntry3 =
+            CommunalWidgetItem(
+                uid = 3L,
+                widgetId = widgetInfo3.widgetId,
+                componentName = widgetInfo3.provider.flattenToString(),
+                itemId = communalItemRankEntry3.uid,
+            )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
index 8532ffe..94b9fa4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
@@ -55,6 +55,7 @@
 private const val KEY_ALT = "TEST_KEY_2"
 private const val USER_MAIN = 0
 private const val USER_GUEST = 10
+private const val PRIVATE_PROFILE = 12
 private const val PACKAGE = "PKG"
 private val INSTANCE_ID = InstanceId.fakeInstanceId(123)!!
 private const val APP_UID = 99
@@ -82,6 +83,7 @@
     private lateinit var mediaDataFilter: MediaDataFilter
     private lateinit var dataMain: MediaData
     private lateinit var dataGuest: MediaData
+    private lateinit var dataPrivateProfile: MediaData
     private val clock = FakeSystemClock()
 
     @Before
@@ -115,6 +117,7 @@
                 appUid = APP_UID
             )
         dataGuest = dataMain.copy(userId = USER_GUEST)
+        dataPrivateProfile = dataMain.copy(userId = PRIVATE_PROFILE)
 
         whenever(smartspaceData.targetId).thenReturn(SMARTSPACE_KEY)
         whenever(smartspaceData.isActive).thenReturn(true)
@@ -130,8 +133,19 @@
 
     private fun setUser(id: Int) {
         whenever(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false)
+        whenever(lockscreenUserManager.isProfileAvailable(anyInt())).thenReturn(false)
         whenever(lockscreenUserManager.isCurrentProfile(eq(id))).thenReturn(true)
-        mediaDataFilter.handleUserSwitched(id)
+        whenever(lockscreenUserManager.isProfileAvailable(eq(id))).thenReturn(true)
+        whenever(lockscreenUserManager.isProfileAvailable(eq(PRIVATE_PROFILE))).thenReturn(true)
+        mediaDataFilter.handleUserSwitched()
+    }
+
+    private fun setPrivateProfileUnavailable() {
+        whenever(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false)
+        whenever(lockscreenUserManager.isCurrentProfile(eq(USER_MAIN))).thenReturn(true)
+        whenever(lockscreenUserManager.isCurrentProfile(eq(PRIVATE_PROFILE))).thenReturn(true)
+        whenever(lockscreenUserManager.isProfileAvailable(eq(PRIVATE_PROFILE))).thenReturn(false)
+        mediaDataFilter.handleProfileChanged()
     }
 
     @Test
@@ -206,6 +220,20 @@
     }
 
     @Test
+    fun testOnProfileChanged_profileUnavailable_loadControls() {
+        // GIVEN that we had some media for both profiles
+        mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
+        mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataPrivateProfile)
+        reset(listener)
+
+        // and we change profile status
+        setPrivateProfileUnavailable()
+
+        // THEN we should add the private profile media
+        verify(listener).onMediaDataRemoved(eq(KEY_ALT))
+    }
+
+    @Test
     fun hasAnyMedia_noMediaSet_returnsFalse() {
         assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
index bcbf666..16c92ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
@@ -18,38 +18,27 @@
 
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_PSS_TASK_SWITCHER
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.mediaprojection.taskswitcher.ui.TaskSwitcherNotificationCoordinator
-import com.android.systemui.util.mockito.whenever
-import org.junit.Before
+import com.android.systemui.util.mockito.mock
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.MockitoAnnotations
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
 class MediaProjectionTaskSwitcherCoreStartableTest : SysuiTestCase() {
 
-    @Mock private lateinit var flags: FeatureFlags
-    @Mock private lateinit var coordinator: TaskSwitcherNotificationCoordinator
+    private val coordinator = mock<TaskSwitcherNotificationCoordinator>()
 
-    private lateinit var coreStartable: MediaProjectionTaskSwitcherCoreStartable
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        coreStartable = MediaProjectionTaskSwitcherCoreStartable(coordinator, flags)
-    }
+    private val coreStartable =
+        MediaProjectionTaskSwitcherCoreStartable(notificationCoordinatorLazy = { coordinator })
 
     @Test
     fun start_flagEnabled_startsCoordinator() {
-        whenever(flags.isEnabled(Flags.PARTIAL_SCREEN_SHARING_TASK_SWITCHER)).thenReturn(true)
+        mSetFlagsRule.enableFlags(FLAG_PSS_TASK_SWITCHER)
 
         coreStartable.start()
 
@@ -58,7 +47,7 @@
 
     @Test
     fun start_flagDisabled_doesNotStartCoordinator() {
-        whenever(flags.isEnabled(Flags.PARTIAL_SCREEN_SHARING_TASK_SWITCHER)).thenReturn(false)
+        mSetFlagsRule.disableFlags(FLAG_PSS_TASK_SWITCHER)
 
         coreStartable.start()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
index 9b61447..c7479fd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
@@ -30,8 +30,8 @@
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.recordissue.RecordIssueDialogDelegate
 import com.android.systemui.res.R
-import com.android.systemui.settings.UserContextProvider
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -65,9 +65,9 @@
     @Mock private lateinit var keyguardDismissUtil: KeyguardDismissUtil
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var dialogLauncherAnimator: DialogLaunchAnimator
-    @Mock private lateinit var dialogFactory: SystemUIDialog.Factory
+    @Mock private lateinit var delegateFactory: RecordIssueDialogDelegate.Factory
+    @Mock private lateinit var dialogDelegate: RecordIssueDialogDelegate
     @Mock private lateinit var dialog: SystemUIDialog
-    @Mock private lateinit var userContextProvider: UserContextProvider
 
     private lateinit var testableLooper: TestableLooper
     private lateinit var tile: RecordIssueTile
@@ -76,7 +76,8 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         whenever(host.context).thenReturn(mContext)
-        whenever(dialogFactory.create(any())).thenReturn(dialog)
+        whenever(delegateFactory.create(any())).thenReturn(dialogDelegate)
+        whenever(dialogDelegate.createDialog()).thenReturn(dialog)
 
         testableLooper = TestableLooper.get(this)
         tile =
@@ -93,8 +94,7 @@
                 keyguardDismissUtil,
                 keyguardStateController,
                 dialogLauncherAnimator,
-                dialogFactory,
-                userContextProvider,
+                delegateFactory,
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index c5d3524..7ce51ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -17,6 +17,9 @@
 package com.android.systemui.recordissue
 
 import android.app.Dialog
+import android.content.Context
+import android.content.SharedPreferences
+import android.os.UserHandle
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.widget.Button
@@ -25,48 +28,107 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.mediaprojection.SessionCreationSource
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
 import com.android.systemui.model.SysUiState
+import com.android.systemui.qs.tiles.RecordIssueTile
 import com.android.systemui.res.R
+import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
 import java.util.concurrent.TimeUnit
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class RecordIssueDialogDelegateTest : SysuiTestCase() {
 
+    @Mock private lateinit var flags: FeatureFlagsClassic
+    @Mock private lateinit var devicePolicyResolver: ScreenCaptureDevicePolicyResolver
+    @Mock private lateinit var dprLazy: dagger.Lazy<ScreenCaptureDevicePolicyResolver>
+    @Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger
+    @Mock private lateinit var userContextProvider: UserContextProvider
+    @Mock private lateinit var userTracker: UserTracker
+    @Mock private lateinit var userFileManager: UserFileManager
+    @Mock private lateinit var sharedPreferences: SharedPreferences
+
+    @Mock private lateinit var sysuiState: SysUiState
+    @Mock private lateinit var systemUIDialogManager: SystemUIDialogManager
+    @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock private lateinit var bgExecutor: Executor
+    @Mock private lateinit var mainExecutor: Executor
+    @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+
     private lateinit var dialog: SystemUIDialog
+    private lateinit var factory: SystemUIDialog.Factory
     private lateinit var latch: CountDownLatch
 
     @Before
     fun setup() {
-        val dialogFactory =
-            SystemUIDialog.Factory(
-                context,
-                mock<FeatureFlags>(),
-                mock<SystemUIDialogManager>(),
-                mock<SysUiState>().apply {
-                    whenever(setFlag(anyInt(), anyBoolean())).thenReturn(this)
-                },
-                mock<BroadcastDispatcher>(),
-                mock<DialogLaunchAnimator>()
+        MockitoAnnotations.initMocks(this)
+        whenever(dprLazy.get()).thenReturn(devicePolicyResolver)
+        whenever(sysuiState.setFlag(anyInt(), anyBoolean())).thenReturn(sysuiState)
+        whenever(userContextProvider.userContext).thenReturn(mContext)
+        whenever(
+                userFileManager.getSharedPreferences(
+                    eq(RecordIssueTile.TILE_SPEC),
+                    eq(Context.MODE_PRIVATE),
+                    anyInt()
+                )
+            )
+            .thenReturn(sharedPreferences)
+
+        factory =
+            spy(
+                SystemUIDialog.Factory(
+                    context,
+                    flags,
+                    systemUIDialogManager,
+                    sysuiState,
+                    broadcastDispatcher,
+                    dialogLaunchAnimator
+                )
             )
 
         latch = CountDownLatch(1)
         dialog =
-            RecordIssueDialogDelegate(dialogFactory, mock()) { latch.countDown() }.createDialog()
+            RecordIssueDialogDelegate(
+                    factory,
+                    userContextProvider,
+                    userTracker,
+                    flags,
+                    bgExecutor,
+                    mainExecutor,
+                    dprLazy,
+                    mediaProjectionMetricsLogger,
+                    userFileManager,
+                ) {
+                    latch.countDown()
+                }
+                .createDialog()
         dialog.show()
     }
 
@@ -91,4 +153,82 @@
         dialog.getButton(Dialog.BUTTON_POSITIVE).callOnClick()
         latch.await(1L, TimeUnit.MILLISECONDS)
     }
+
+    @Test
+    fun screenCaptureDisabledDialog_isShown_whenFunctionalityIsDisabled() {
+        whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
+            .thenReturn(true)
+        whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
+            .thenReturn(true)
+
+        val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
+        screenRecordSwitch.isChecked = true
+
+        val bgCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+        verify(bgExecutor).execute(bgCaptor.capture())
+        bgCaptor.value.run()
+
+        val mainCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+        verify(mainExecutor).execute(mainCaptor.capture())
+        mainCaptor.value.run()
+
+        verify(mediaProjectionMetricsLogger, never())
+            .notifyProjectionInitiated(
+                anyInt(),
+                eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+            )
+        assertThat(screenRecordSwitch.isChecked).isFalse()
+    }
+
+    @Test
+    fun screenCapturePermissionDialog_isShown_correctly() {
+        whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
+            .thenReturn(false)
+        whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
+            .thenReturn(false)
+        whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
+        whenever(sharedPreferences.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false))
+            .thenReturn(false)
+
+        val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
+        screenRecordSwitch.isChecked = true
+
+        val bgCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+        verify(bgExecutor).execute(bgCaptor.capture())
+        bgCaptor.value.run()
+
+        val mainCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+        verify(mainExecutor).execute(mainCaptor.capture())
+        mainCaptor.value.run()
+
+        verify(mediaProjectionMetricsLogger)
+            .notifyProjectionInitiated(
+                anyInt(),
+                eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+            )
+        verify(factory).create(any<ScreenCapturePermissionDialogDelegate>())
+    }
+
+    @Test
+    fun noDialogsAreShown_forScreenRecord_whenApprovalIsAlreadyGiven() {
+        whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
+            .thenReturn(false)
+        whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
+            .thenReturn(false)
+        whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(false)
+
+        val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
+        screenRecordSwitch.isChecked = true
+
+        val bgCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+        verify(bgExecutor).execute(bgCaptor.capture())
+        bgCaptor.value.run()
+
+        verify(mediaProjectionMetricsLogger)
+            .notifyProjectionInitiated(
+                anyInt(),
+                eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+            )
+        verify(factory, never()).create(any<ScreenCapturePermissionDialogDelegate>())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index c32d259..032ec74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -177,7 +177,7 @@
         verify(context)
                 .registerReceiverForAllUsers(eq(tracker), capture(captor), isNull(), eq(handler))
         with(captor.value) {
-            assertThat(countActions()).isEqualTo(7)
+            assertThat(countActions()).isEqualTo(11)
             assertThat(hasAction(Intent.ACTION_LOCALE_CHANGED)).isTrue()
             assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
             assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
@@ -185,6 +185,10 @@
             assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_ADDED)).isTrue()
             assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)).isTrue()
             assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)).isTrue()
+            assertThat(hasAction(Intent.ACTION_PROFILE_ADDED)).isTrue()
+            assertThat(hasAction(Intent.ACTION_PROFILE_REMOVED)).isTrue()
+            assertThat(hasAction(Intent.ACTION_PROFILE_AVAILABLE)).isTrue()
+            assertThat(hasAction(Intent.ACTION_PROFILE_UNAVAILABLE)).isTrue()
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index ee94cbb..ee27c5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.flags.Flags.TRANSIT_CLOCK
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockId
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockMetadata
 import com.android.systemui.plugins.clocks.ClockProviderPlugin
 import com.android.systemui.plugins.clocks.ClockSettings
@@ -128,6 +129,7 @@
         override fun createClock(settings: ClockSettings): ClockController =
             createCallbacks[settings.clockId!!]!!(settings.clockId!!)
         override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!(id)
+        override fun initialize(buffers: ClockMessageBuffers?) { }
 
         fun addClock(
             id: ClockId,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index fef262f..e0e8d1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -26,6 +26,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.customization.R
+import com.android.systemui.plugins.clocks.ClockId
 import com.android.systemui.plugins.clocks.ClockSettings
 import com.android.systemui.shared.clocks.DefaultClockController.Companion.DOZE_COLOR
 import com.android.systemui.util.mockito.any
@@ -49,6 +50,9 @@
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
 
+private fun DefaultClockProvider.createClock(id: ClockId): DefaultClockController =
+    createClock(ClockSettings(id, null)) as DefaultClockController
+
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
 class DefaultClockProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 2bee7b8..d3febf5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -41,6 +41,7 @@
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
 import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository;
 import com.android.systemui.statusbar.domain.interactor.SilentNotificationStatusIconsVisibilityInteractor;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -151,6 +152,7 @@
 
     @Test
     public void testOnConnectReadStatusBarSetting() {
+        mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME);
         NotificationListener.NotificationSettingsListener settingsListener =
                 mock(NotificationListener.NotificationSettingsListener.class);
         mListener.addNotificationSettingsListener(settingsListener);
@@ -164,6 +166,7 @@
 
     @Test
     public void testOnStatusBarIconsBehaviorChanged() {
+        mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME);
         NotificationListener.NotificationSettingsListener settingsListener =
                 mock(NotificationListener.NotificationSettingsListener.class);
         mListener.addNotificationSettingsListener(settingsListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 2df6e46..d4300f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -17,6 +17,7 @@
 import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.StackScrollAlgorithmState
 import com.android.systemui.util.mockito.mock
 import junit.framework.Assert.assertEquals
@@ -71,6 +72,7 @@
 
     @Test
     fun testShadeWidth_BasedOnFractionToShade() {
+        mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME)
         setFractionToShade(0f)
         setOnLockscreen(true)
 
@@ -86,6 +88,7 @@
 
     @Test
     fun testShelfIsLong_WhenNotOnLockscreen() {
+        mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME)
         setFractionToShade(0f)
         setOnLockscreen(false)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
index 62d8f7f..9f4e1dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
 import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
@@ -73,6 +74,7 @@
 
     @Test
     fun calculateWidthFor_fiveIcons_widthForFourIcons() {
+        mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME)
         iconContainer.setActualPaddingStart(10f)
         iconContainer.setActualPaddingEnd(10f)
         iconContainer.setIconSize(10)
@@ -151,7 +153,7 @@
         iconContainer.addView(iconFive)
         assertEquals(5, iconContainer.childCount)
 
-        val width = iconContainer.calculateWidthFor(/* numIcons= */ 5f)
+        val width = iconContainer.calculateWidthFor(/* numIcons= */ 4f)
         iconContainer.setActualLayoutWidth(width.toInt())
 
         iconContainer.calculateIconXTranslations()
@@ -212,6 +214,7 @@
 
     @Test
     fun shouldForceOverflow_appearingAboveSpeedBump_true() {
+        mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME)
         val forceOverflow =
             iconContainer.shouldForceOverflow(
                 /* i= */ 1,
@@ -228,7 +231,7 @@
             iconContainer.shouldForceOverflow(
                 /* i= */ 10,
                 /* speedBumpIndex= */ 11,
-                /* iconAppearAmount= */ 0f,
+                /* iconAppearAmount= */ 0.1f,
                 /* maxVisibleIcons= */ 5
             )
         assertTrue(forceOverflow)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
index 7594c90..feff046 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.statusbar.phone
 
 import android.graphics.Point
+import android.testing.TestableLooper
 import android.view.Display
 import android.view.Surface
 import android.view.View
@@ -19,6 +20,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
+@TestableLooper.RunWithLooper
 class StatusBarMoveFromCenterAnimationControllerTest : SysuiTestCase() {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 14751c2..54d3607 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -36,6 +36,7 @@
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
 import android.view.View;
 
 import androidx.test.filters.SmallTest;
@@ -91,7 +92,6 @@
 
     private NotificationIconAreaController mMockNotificationAreaController;
     private ShadeExpansionStateManager mShadeExpansionStateManager;
-    private View mNotificationAreaInner;
     private OngoingCallController mOngoingCallController;
     private SystemStatusAnimationScheduler mAnimationScheduler;
     private StatusBarLocationPublisher mLocationPublisher;
@@ -270,15 +270,15 @@
 
         fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
 
-        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+        assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
-        assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+        assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
 
         fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
 
-        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+        assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
     }
 
     @Test
@@ -310,7 +310,7 @@
 
         // THEN all views are hidden
         assertEquals(View.INVISIBLE, getClockView().getVisibility());
-        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+        assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
     }
 
@@ -326,7 +326,7 @@
 
         // THEN all views are shown
         assertEquals(View.VISIBLE, getClockView().getVisibility());
-        assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+        assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
         assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
     }
 
@@ -343,7 +343,7 @@
 
         // THEN all views are hidden
         assertEquals(View.INVISIBLE, getClockView().getVisibility());
-        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+        assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
 
         // WHEN the shade is updated to no longer be open
@@ -354,7 +354,7 @@
 
         // THEN all views are shown
         assertEquals(View.VISIBLE, getClockView().getVisibility());
-        assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+        assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
         assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
     }
 
@@ -368,7 +368,7 @@
 
         // THEN all views are shown
         assertEquals(View.VISIBLE, getClockView().getVisibility());
-        assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+        assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
         assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
     }
 
@@ -382,7 +382,7 @@
 
         // THEN all views are hidden
         assertEquals(View.GONE, getClockView().getVisibility());
-        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+        assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
     }
 
@@ -396,7 +396,7 @@
 
         // THEN all views are hidden
         assertEquals(View.GONE, getClockView().getVisibility());
-        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+        assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
 
         // WHEN the transition has finished
@@ -405,7 +405,7 @@
 
         // THEN all views are shown
         assertEquals(View.VISIBLE, getClockView().getVisibility());
-        assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+        assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
         assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
     }
 
@@ -438,7 +438,7 @@
 
         assertEquals(View.VISIBLE,
                 mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
-        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+        assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
     }
 
     @Test
@@ -503,8 +503,8 @@
         fragment.disable(DEFAULT_DISPLAY, 0, 0, true);
 
         // Notification area is hidden without delay
-        assertEquals(0f, mNotificationAreaInner.getAlpha(), 0.01);
-        assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+        assertEquals(0f, getNotificationAreaView().getAlpha(), 0.01);
+        assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
     }
 
     @Test
@@ -723,11 +723,10 @@
 
     private void setUpNotificationIconAreaController() {
         mMockNotificationAreaController = mock(NotificationIconAreaController.class);
-
-        mNotificationAreaInner = new View(mContext);
-
-        when(mMockNotificationAreaController.getNotificationInnerAreaView()).thenReturn(
-                mNotificationAreaInner);
+        View notificationAreaInner =
+                LayoutInflater.from(mContext).inflate(R.layout.notification_icon_area, null);
+        when(mMockNotificationAreaController.getNotificationInnerAreaView())
+                .thenReturn(notificationAreaInner);
     }
 
     /**
@@ -782,4 +781,8 @@
     private View getEndSideContentView() {
         return mFragment.getView().findViewById(R.id.status_bar_end_side_content);
     }
+
+    private View getNotificationAreaView() {
+        return mFragment.getView().findViewById(R.id.notificationIcons);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
index e461e3f..bbc96f70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
@@ -26,8 +26,11 @@
 
     private val recordings: MutableList<UnfoldTransitionRecording> = arrayListOf()
     private var currentRecording: UnfoldTransitionRecording? = null
+    var lastCallbackThread: Thread? = null
+        private set
 
     override fun onTransitionStarted() {
+        lastCallbackThread = Thread.currentThread()
         assertWithMessage("Trying to start a transition when it is already in progress")
             .that(currentRecording)
             .isNull()
@@ -36,6 +39,7 @@
     }
 
     override fun onTransitionProgress(progress: Float) {
+        lastCallbackThread = Thread.currentThread()
         assertWithMessage("Received transition progress event when it's not started")
             .that(currentRecording)
             .isNotNull()
@@ -43,6 +47,7 @@
     }
 
     override fun onTransitionFinishing() {
+        lastCallbackThread = Thread.currentThread()
         assertWithMessage("Received transition finishing event when it's not started")
             .that(currentRecording)
             .isNotNull()
@@ -50,6 +55,7 @@
     }
 
     override fun onTransitionFinished() {
+        lastCallbackThread = Thread.currentThread()
         assertWithMessage("Received transition finish event when it's not started")
             .that(currentRecording)
             .isNotNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
index a25469b..d864d53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.unfold.util
 
 import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
 import android.view.Surface
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -37,6 +38,7 @@
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
+@TestableLooper.RunWithLooper
 class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
 
     @Mock lateinit var rotationChangeProvider: RotationChangeProvider
@@ -48,10 +50,12 @@
     @Captor private lateinit var rotationListenerCaptor: ArgumentCaptor<RotationListener>
 
     lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
+    private lateinit var testableLooper : TestableLooper
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
 
         progressProvider =
             NaturalRotationUnfoldProgressProvider(context, rotationChangeProvider, sourceProvider)
@@ -123,5 +127,6 @@
 
     private fun onRotationChanged(rotation: Int) {
         rotationListenerCaptor.value.onRotationChanged(rotation)
+        testableLooper.processAllMessages()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
index e1e54a9..2f29b3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
@@ -19,6 +19,7 @@
 import android.database.ContentObserver
 import android.provider.Settings
 import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.unfold.TestUnfoldTransitionProvider
@@ -36,6 +37,7 @@
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
+@TestableLooper.RunWithLooper
 class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
 
     @Mock lateinit var sinkProvider: TransitionProgressListener
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
new file mode 100644
index 0000000..5b4f4d3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
@@ -0,0 +1,161 @@
+/*
+ * 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.unfold.util
+
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Process
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.progress.TestUnfoldProgressListener
+import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.CancellableContinuation
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.withTimeout
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class ScopedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
+
+    private val rootProvider = TestUnfoldTransitionProvider()
+    private val listener = TestUnfoldProgressListener()
+    private val testScope = TestScope(UnconfinedTestDispatcher())
+    private val bgThread =
+        HandlerThread("UnfoldBgTest", Process.THREAD_PRIORITY_FOREGROUND).apply { start() }
+    private val bgHandler = Handler(bgThread.looper)
+    private val scopedProvider =
+        ScopedUnfoldTransitionProgressProvider(rootProvider).apply { addCallback(listener) }
+
+    @Test
+    fun setReadyToHandleTransition_whileTransitionRunning_propagatesCallbacks() =
+        testScope.runTest {
+            runBlockingInBg { rootProvider.onTransitionStarted() }
+
+            scopedProvider.setReadyToHandleTransition(true)
+
+            runBlockingInBg { /* sync barrier */}
+
+            listener.assertStarted()
+
+            runBlockingInBg { rootProvider.onTransitionProgress(1f) }
+
+            listener.assertLastProgress(1f)
+
+            runBlockingInBg { rootProvider.onTransitionFinished() }
+
+            listener.assertNotStarted()
+        }
+
+    @Test
+    fun setReadyToHandleTransition_whileTransitionNotRunning_callbacksInProgressThread() {
+        testScope.runTest {
+            scopedProvider.setReadyToHandleTransition(true)
+
+            val bgThread = runBlockingInBg { Thread.currentThread() }
+
+            runBlockingInBg { rootProvider.onTransitionStarted() }
+
+            listener.assertStarted()
+
+            assertThat(listener.lastCallbackThread).isEqualTo(bgThread)
+        }
+    }
+
+    @Test
+    fun setReadyToHandleTransition_beforeAnyCallback_doesNotCrash() {
+        testScope.runTest { scopedProvider.setReadyToHandleTransition(true) }
+    }
+
+    @Test
+    fun onTransitionStarted_whileNotReadyToHandleTransition_doesNotPropagate() {
+        testScope.runTest {
+            scopedProvider.setReadyToHandleTransition(false)
+
+            rootProvider.onTransitionStarted()
+
+            listener.assertNotStarted()
+        }
+    }
+
+    @Test
+    fun onTransitionStarted_defaultReadiness_doesNotPropagate() {
+        testScope.runTest {
+            rootProvider.onTransitionStarted()
+
+            listener.assertNotStarted()
+        }
+    }
+
+    @Test
+    fun onTransitionStarted_fromDifferentThreads_throws() {
+        testScope.runTest {
+            runBlockingInBg {
+                rootProvider.onTransitionStarted()
+                rootProvider.onTransitionFinished()
+            }
+            assertThrows(IllegalStateException::class.java) { rootProvider.onTransitionStarted() }
+        }
+    }
+
+    @Test
+    fun onTransitionProgress_fromDifferentThreads_throws() {
+        testScope.runTest {
+            runBlockingInBg { rootProvider.onTransitionStarted() }
+            assertThrows(IllegalStateException::class.java) {
+                rootProvider.onTransitionProgress(1f)
+            }
+        }
+    }
+
+    @Test
+    fun onTransitionFinished_fromDifferentThreads_throws() {
+        testScope.runTest {
+            runBlockingInBg { rootProvider.onTransitionStarted() }
+            assertThrows(IllegalStateException::class.java) { rootProvider.onTransitionFinished() }
+        }
+    }
+
+    @Test
+    fun onTransitionFinishing_fromDifferentThreads_throws() {
+        testScope.runTest {
+            runBlockingInBg { rootProvider.onTransitionStarted() }
+            assertThrows(IllegalStateException::class.java) { rootProvider.onTransitionFinishing() }
+        }
+    }
+
+    private fun <T> runBlockingInBg(f: () -> T): T {
+        return runBlocking {
+            withTimeout(5.seconds) {
+                suspendCancellableCoroutine { c: CancellableContinuation<T> ->
+                    bgHandler.post { c.resumeWith(Result.success(f())) }
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
index 4a38fc0..f484ea0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.unfold.util
 
 import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.unfold.TestUnfoldTransitionProvider
@@ -27,6 +28,7 @@
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
+@TestableLooper.RunWithLooper
 class UnfoldOnlyProgressProviderTest : SysuiTestCase() {
 
     private val listener = TestUnfoldProgressListener()
@@ -54,9 +56,7 @@
         sourceProvider.onTransitionProgress(0.5f)
         sourceProvider.onTransitionFinished()
 
-        with(listener.ensureTransitionFinished()) {
-            assertLastProgress(0.5f)
-        }
+        with(listener.ensureTransitionFinished()) { assertLastProgress(0.5f) }
     }
 
     @Test
@@ -121,8 +121,6 @@
         sourceProvider.onTransitionProgress(0.1f)
         sourceProvider.onTransitionFinished()
 
-        with(listener.ensureTransitionFinished()) {
-            assertLastProgress(0.1f)
-        }
+        with(listener.ensureTransitionFinished()) { assertLastProgress(0.1f) }
     }
 }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
index f9751d9..2bca272 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
@@ -15,8 +15,11 @@
  */
 package com.android.systemui.unfold.util
 
+import android.os.Handler
+import android.os.Looper
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import java.util.concurrent.CopyOnWriteArrayList
 
 /**
  * Manages progress listeners that can have smaller lifespan than the unfold animation.
@@ -33,12 +36,13 @@
 constructor(source: UnfoldTransitionProgressProvider? = null) :
     UnfoldTransitionProgressProvider, TransitionProgressListener {
 
+    private var progressHandler: Handler? = null
     private var source: UnfoldTransitionProgressProvider? = null
 
-    private val listeners: MutableList<TransitionProgressListener> = mutableListOf()
+    private val listeners = CopyOnWriteArrayList<TransitionProgressListener>()
 
-    private var isReadyToHandleTransition = false
-    private var isTransitionRunning = false
+    @Volatile private var isReadyToHandleTransition = false
+    @Volatile private var isTransitionRunning = false
     private var lastTransitionProgress = PROGRESS_UNSET
 
     init {
@@ -70,15 +74,18 @@
      * Call it with readyToHandleTransition = false when listeners can't process the events.
      */
     fun setReadyToHandleTransition(isReadyToHandleTransition: Boolean) {
-        if (isTransitionRunning) {
-            if (isReadyToHandleTransition) {
-                listeners.forEach { it.onTransitionStarted() }
-                if (lastTransitionProgress != PROGRESS_UNSET) {
-                    listeners.forEach { it.onTransitionProgress(lastTransitionProgress) }
+        val progressHandler = this.progressHandler
+        if (isTransitionRunning && progressHandler != null) {
+            progressHandler.post {
+                if (isReadyToHandleTransition) {
+                    listeners.forEach { it.onTransitionStarted() }
+                    if (lastTransitionProgress != PROGRESS_UNSET) {
+                        listeners.forEach { it.onTransitionProgress(lastTransitionProgress) }
+                    }
+                } else {
+                    isTransitionRunning = false
+                    listeners.forEach { it.onTransitionFinished() }
                 }
-            } else {
-                isTransitionRunning = false
-                listeners.forEach { it.onTransitionFinished() }
             }
         }
         this.isReadyToHandleTransition = isReadyToHandleTransition
@@ -98,6 +105,7 @@
     }
 
     override fun onTransitionStarted() {
+        assertInProgressThread()
         isTransitionRunning = true
         if (isReadyToHandleTransition) {
             listeners.forEach { it.onTransitionStarted() }
@@ -105,6 +113,7 @@
     }
 
     override fun onTransitionProgress(progress: Float) {
+        assertInProgressThread()
         if (isReadyToHandleTransition) {
             listeners.forEach { it.onTransitionProgress(progress) }
         }
@@ -112,12 +121,14 @@
     }
 
     override fun onTransitionFinishing() {
+        assertInProgressThread()
         if (isReadyToHandleTransition) {
             listeners.forEach { it.onTransitionFinishing() }
         }
     }
 
     override fun onTransitionFinished() {
+        assertInProgressThread()
         if (isReadyToHandleTransition) {
             listeners.forEach { it.onTransitionFinished() }
         }
@@ -125,6 +136,21 @@
         lastTransitionProgress = PROGRESS_UNSET
     }
 
+    private fun assertInProgressThread() {
+        val cachedProgressHandler = progressHandler
+        if (cachedProgressHandler == null) {
+            val thisLooper = Looper.myLooper() ?: error("This thread is expected to have a looper.")
+            progressHandler = Handler(thisLooper)
+        } else {
+            check(cachedProgressHandler.looper.isCurrentThread) {
+                """Receiving unfold transition callback from different threads.
+                    |Current: ${Thread.currentThread()}
+                    |expected: ${cachedProgressHandler.looper.thread}"""
+                    .trimMargin()
+            }
+        }
+    }
+
     companion object {
         private const val PROGRESS_UNSET = -1f
     }
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index a4b2896..cab2d74 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.server.appwidget;
 
+import static android.appwidget.flags.Flags.removeAppWidgetServiceIoFromCriticalPath;
 import static android.content.Context.KEYGUARD_SERVICE;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -144,6 +145,7 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
@@ -277,7 +279,12 @@
         mKeyguardManager = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
         mDevicePolicyManagerInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
-        mSaveStateHandler = BackgroundThread.getHandler();
+        if (removeAppWidgetServiceIoFromCriticalPath()) {
+            mSaveStateHandler = new Handler(BackgroundThread.get().getLooper(),
+                    this::handleSaveMessage);
+        } else {
+            mSaveStateHandler = BackgroundThread.getHandler();
+        }
         final ServiceThread serviceThread = new ServiceThread(TAG,
                 android.os.Process.THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
         serviceThread.start();
@@ -314,6 +321,40 @@
         mMaxWidgetBitmapMemory = 6 * size.x * size.y;
     }
 
+    private boolean handleSaveMessage(Message msg) {
+        final int userId = msg.what;
+        SparseArray<byte[]> userIdToBytesMapping;
+        synchronized (mLock) {
+            // No need to enforce unlocked state when there is no caller. User can be in the
+            // stopping state or removed by the time the message is processed
+            ensureGroupStateLoadedLocked(userId, false /* enforceUserUnlockingOrUnlocked */);
+            userIdToBytesMapping = saveStateToByteArrayLocked(userId);
+        }
+
+        for (int i = 0; i < userIdToBytesMapping.size(); i++) {
+            int currentProfileId = userIdToBytesMapping.keyAt(i);
+            byte[] currentStateByteArray = userIdToBytesMapping.valueAt(i);
+            AtomicFile currentFile = getSavedStateFile(currentProfileId);
+            FileOutputStream fileStream;
+            try {
+                fileStream = currentFile.startWrite();
+            } catch (IOException e) {
+                Log.e(TAG, "Failed to start writing stream", e);
+                continue;
+            }
+
+            try {
+                fileStream.write(currentStateByteArray);
+                currentFile.finishWrite(fileStream);
+            } catch (IOException e) {
+                Log.e(TAG, "Failed to write state byte stream to file", e);
+                currentFile.failWrite(fileStream);
+            }
+        }
+
+        return true;
+    }
+
     private void registerBroadcastReceiver() {
         // Register for broadcasts about package install, etc., so we can
         // update the provider list.
@@ -1944,7 +1985,12 @@
     }
 
     private void saveGroupStateAsync(int groupId) {
-        mSaveStateHandler.post(new SaveStateRunnable(groupId));
+        if (removeAppWidgetServiceIoFromCriticalPath()) {
+            mSaveStateHandler.removeMessages(groupId);
+            mSaveStateHandler.sendEmptyMessage(groupId);
+        } else {
+            mSaveStateHandler.post(new SaveStateRunnable(groupId));
+        }
     }
 
     private void updateAppWidgetInstanceLocked(Widget widget, RemoteViews views,
@@ -3104,6 +3150,23 @@
     }
 
     @GuardedBy("mLock")
+    private @NonNull SparseArray<byte[]> saveStateToByteArrayLocked(int userId) {
+        tagProvidersAndHosts();
+
+        final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
+        SparseArray<byte[]> userIdToBytesMapping = new SparseArray<>();
+
+        for (int profileId : profileIds) {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            if (writeProfileStateToStreamLocked(outputStream, profileId)) {
+                userIdToBytesMapping.put(profileId, outputStream.toByteArray());
+            }
+        }
+
+        return userIdToBytesMapping;
+    }
+
+    @GuardedBy("mLock")
     private void saveStateLocked(int userId) {
         tagProvidersAndHosts();
 
@@ -3117,7 +3180,7 @@
             FileOutputStream stream;
             try {
                 stream = file.startWrite();
-                if (writeProfileStateToFileLocked(stream, profileId)) {
+                if (writeProfileStateToStreamLocked(stream, profileId)) {
                     file.finishWrite(stream);
                 } else {
                     file.failWrite(stream);
@@ -3158,7 +3221,7 @@
     }
 
     @GuardedBy("mLock")
-    private boolean writeProfileStateToFileLocked(FileOutputStream stream, int userId) {
+    private boolean writeProfileStateToStreamLocked(OutputStream stream, int userId) {
         int N;
 
         try {
diff --git a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
index d9741c8..4a6d5c9b 100644
--- a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
+++ b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
@@ -25,9 +25,6 @@
 import android.service.autofill.FillResponse;
 import android.util.Slog;
 
-import java.util.Objects;
-
-
 /**
  * Requests autofill response from a Remote Autofill Service. This autofill service can be
  * either a Credential Autofill Service or the user-opted autofill service.
@@ -51,7 +48,6 @@
 
     private final RemoteFillService mRemoteFillService;
     private final SecondaryProviderCallback mCallback;
-    private FillRequest mLastFillRequest;
     private int mLastFlag;
 
     SecondaryProviderHandler(
@@ -97,17 +93,11 @@
     }
 
     /**
-     * Requests a new fill response. If the fill request is same as the last requested fill request,
-     * then the request is duped.
+     * Requests a new fill response.
      */
     public void onFillRequest(FillRequest pendingFillRequest, int flag) {
-        if (Objects.equals(pendingFillRequest, mLastFillRequest)) {
-            Slog.v(TAG, "Deduping fill request to secondary provider.");
-            return;
-        }
         Slog.v(TAG, "Requesting fill response to secondary provider.");
         mLastFlag = flag;
-        mLastFillRequest = pendingFillRequest;
         mRemoteFillService.onFillRequest(pendingFillRequest);
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d527ce0..c4e8f12 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -367,6 +367,9 @@
     @GuardedBy("mLock")
     private SparseArray<FillResponse> mResponses;
 
+    @GuardedBy("mLock")
+    private SparseArray<FillResponse> mSecondaryResponses;
+
     /**
      * Contexts read from the app; they will be updated (sanitized, change values for save) before
      * sent to {@link AutofillService}. Ordered by the time they were read.
@@ -713,7 +716,14 @@
                         mPendingFillRequest.getDelayedFillIntentSender());
             }
             mLastFillRequest = mPendingFillRequest;
-            mRemoteFillService.onFillRequest(mPendingFillRequest);
+            if (shouldRequestSecondaryProvider(mPendingFillRequest.getFlags())
+                    && mSecondaryProviderHandler != null) {
+                Slog.v(TAG, "Requesting fill response to secondary provider.");
+                mSecondaryProviderHandler.onFillRequest(mPendingFillRequest,
+                        mPendingFillRequest.getFlags());
+            } else if (mRemoteFillService != null) {
+                mRemoteFillService.onFillRequest(mPendingFillRequest);
+            }
             mPendingInlineSuggestionsRequest = null;
             mWaitForInlineRequest = false;
             mPendingFillRequest = null;
@@ -1196,7 +1206,8 @@
     @GuardedBy("mLock")
     private void requestNewFillResponseLocked(@NonNull ViewState viewState, int newState,
             int flags) {
-        final FillResponse existingResponse = viewState.getResponse();
+        final FillResponse existingResponse = shouldRequestSecondaryProvider(flags)
+                ? viewState.getSecondaryResponse() : viewState.getResponse();
         mFillRequestEventLogger.startLogForNewRequest();
         mRequestCount++;
         mFillRequestEventLogger.maybeSetAppPackageUid(uid);
@@ -1804,6 +1815,10 @@
             return;
         }
         synchronized (mLock) {
+            if (mSecondaryResponses == null) {
+                mSecondaryResponses = new SparseArray<>(2);
+            }
+            mSecondaryResponses.put(fillResponse.getRequestId(), fillResponse);
             setViewStatesLocked(fillResponse, ViewState.STATE_FILLABLE, /* clearResponse= */ false,
                     /* isPrimary= */ false);
 
@@ -3980,7 +3995,7 @@
         }
 
         // If it's not, then check if it should start a partition.
-        if (shouldStartNewPartitionLocked(id)) {
+        if (shouldStartNewPartitionLocked(id, flags)) {
             if (sDebug) {
                 Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": "
                         + viewState.getStateAsString());
@@ -4008,9 +4023,11 @@
      * @return {@code true} if a new partition should be started
      */
     @GuardedBy("mLock")
-    private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id) {
+    private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id, int flags) {
         final ViewState currentView = mViewStates.get(id);
-        if (mResponses == null) {
+        SparseArray<FillResponse> responses = shouldRequestSecondaryProvider(flags)
+                ? mSecondaryResponses : mResponses;
+        if (responses == null) {
             return currentView != null && (currentView.getState()
                     & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) == 0;
         }
@@ -4022,7 +4039,7 @@
             return true;
         }
 
-        final int numResponses = mResponses.size();
+        final int numResponses = responses.size();
         if (numResponses >= AutofillManagerService.getPartitionMaxCount()) {
             Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id
                     + " reached maximum of " + AutofillManagerService.getPartitionMaxCount());
@@ -4030,7 +4047,7 @@
         }
 
         for (int responseNum = 0; responseNum < numResponses; responseNum++) {
-            final FillResponse response = mResponses.valueAt(responseNum);
+            final FillResponse response = responses.valueAt(responseNum);
 
             if (ArrayUtils.contains(response.getIgnoredIds(), id)) {
                 return false;
@@ -4066,6 +4083,10 @@
     }
 
     boolean shouldRequestSecondaryProvider(int flags) {
+        if (!mService.isAutofillCredmanIntegrationEnabled()
+                || mSecondaryProviderHandler == null) {
+            return false;
+        }
         if (mIsPrimaryCredential) {
             return (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) == 0;
         } else {
@@ -4205,12 +4226,6 @@
                 }
                 break;
             case ACTION_VIEW_ENTERED:
-                if (shouldRequestSecondaryProvider(flags)
-                        && mSecondaryProviderHandler != null
-                        && mAssistReceiver.mLastFillRequest != null) {
-                    mSecondaryProviderHandler.onFillRequest(mAssistReceiver.mLastFillRequest,
-                            flags);
-                }
                 mLatencyBaseTime = SystemClock.elapsedRealtime();
                 boolean wasPreviouslyFillDialog = mPreviouslyFillDialogPotentiallyStarted;
                 mPreviouslyFillDialogPotentiallyStarted = false;
@@ -4225,6 +4240,19 @@
                     viewState.setCurrentValue(value);
                 }
 
+                if (shouldRequestSecondaryProvider(flags)) {
+                    if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(
+                            id, viewState, flags)) {
+                        Slog.v(TAG, "Started a new fill request for secondary provider.");
+                        return;
+                    }
+                    // If the ViewState is ready to be displayed, onReady() will be called.
+                    viewState.update(value, virtualBounds, flags);
+
+                    // return here because primary provider logic is not applicable.
+                    return;
+                }
+
                 if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
                     if (sDebug) Slog.d(TAG, "Ignoring VIEW_ENTERED on URL BAR (id=" + id + ")");
                     return;
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index b0bb9ec..fec5aa5 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -162,6 +162,11 @@
         return mPrimaryFillResponse;
     }
 
+    @Nullable
+    FillResponse getSecondaryResponse() {
+        return mSecondaryFillResponse;
+    }
+
     void setResponse(FillResponse response) {
         setResponse(response, /* isPrimary= */ true);
     }
diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/PackageUtils.java
index db40fc4..6c77018 100644
--- a/services/companion/java/com/android/server/companion/PackageUtils.java
+++ b/services/companion/java/com/android/server/companion/PackageUtils.java
@@ -19,6 +19,7 @@
 import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
 import static android.content.pm.PackageManager.GET_CONFIGURATIONS;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.os.Binder.getCallingUid;
 
 import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
 import static com.android.server.companion.CompanionDeviceManagerService.TAG;
@@ -41,6 +42,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.Signature;
 import android.os.Binder;
+import android.os.Process;
 import android.util.Log;
 import android.util.Slog;
 
@@ -80,6 +82,11 @@
 
     static void enforceUsesCompanionDeviceFeature(@NonNull Context context,
             @UserIdInt int userId, @NonNull String packageName) {
+        // Allow system server to create CDM associations without FEATURE_COMPANION_DEVICE_SETUP
+        if (getCallingUid() == Process.SYSTEM_UID) {
+            return;
+        }
+
         String requiredFeature = FEATURE_COMPANION_DEVICE_SETUP;
 
         FeatureInfo[] requestedFeatures = getPackageInfo(context, userId, packageName).reqFeatures;
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index c258370..e289a56 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -27,6 +27,7 @@
 per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS
 per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS
 per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
+per-file *SecurityStateManager* = file:/SECURITY_STATE_OWNERS
 per-file *SoundTrigger* = file:/media/java/android/media/soundtrigger/OWNERS
 per-file *Storage* = file:/core/java/android/os/storage/OWNERS
 per-file *TimeUpdate* = file:/services/core/java/com/android/server/timezonedetector/OWNERS
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 3ae55271..d461643 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -8303,7 +8303,7 @@
                 r.mFgsNotificationShown,
                 durationMs,
                 r.mStartForegroundCount,
-                ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName),
+                0, // Short instance name -- no longer logging it.
                 r.mFgsHasNotificationPermission,
                 r.foregroundServiceType,
                 fgsTypeCheckCode,
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 3ce91c8..1d69905 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -225,7 +225,7 @@
     /**
      * The default value to {@link #KEY_ENABLE_NEW_OOMADJ}.
      */
-    private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite();
+    private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = false;
 
     /**
      * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a80d2fd..f8451fd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -164,6 +164,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.MemoryStatUtil.hasMemcg;
 import static com.android.server.am.ProcessList.ProcStartHandler;
+import static com.android.server.flags.Flags.disableSystemCompaction;
 import static com.android.server.net.NetworkPolicyManagerInternal.updateBlockedReasonsWithProcState;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 import static com.android.server.pm.UserManagerInternal.USER_START_MODE_BACKGROUND;
@@ -8564,8 +8565,10 @@
             final long now = SystemClock.uptimeMillis();
             final long timeSinceLastIdle = now - mLastIdleTime;
 
-            // Compact all non-zygote processes to freshen up the page cache.
-            mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
+            if (!disableSystemCompaction()) {
+                // Compact all non-zygote processes to freshen up the page cache.
+                mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
+            }
 
             final long lowRamSinceLastIdle = mAppProfiler.getLowRamTimeSinceIdleLPr(now);
             mLastIdleTime = now;
diff --git a/services/core/java/com/android/server/am/ActivityManagerUtils.java b/services/core/java/com/android/server/am/ActivityManagerUtils.java
index 01466b8..78a2ecb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerUtils.java
+++ b/services/core/java/com/android/server/am/ActivityManagerUtils.java
@@ -129,14 +129,6 @@
     }
 
     /**
-     * @param shortInstanceName {@link ServiceRecord#shortInstanceName}.
-     * @return hash of the ServiceRecord's shortInstanceName, combined with ANDROID_ID.
-     */
-    public static int hashComponentNameForAtom(String shortInstanceName) {
-        return getUnsignedHashUnCached(shortInstanceName) ^ getAndroidIdHash();
-    }
-
-    /**
      * Helper method to log an unsafe intent event.
      */
     public static void logUnsafeIntentEvent(int event, int callingUid,
diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
index 05303f6f..b68572f 100644
--- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
+++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
@@ -519,7 +519,7 @@
                 r.mFgsNotificationShown,
                 0, // durationMs
                 r.mStartForegroundCount,
-                ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName),
+                0, // Short instance name -- no longer logging it.
                 r.mFgsHasNotificationPermission,
                 r.foregroundServiceType,
                 0,
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 3e1edf2..cc56110 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -141,6 +141,7 @@
         "context_hub",
         "core_experiments_team_internal",
         "core_graphics",
+        "core_libraries",
         "dck_framework",
         "devoptions_settings",
         "game",
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index ea791b7..37fe389 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -44,6 +44,7 @@
 import static com.android.media.audio.Flags.alarmMinVolumeZero;
 import static com.android.media.audio.Flags.bluetoothMacAddressAnonymization;
 import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume;
+import static com.android.media.audio.Flags.ringerModeAffectsAlarm;
 import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE;
 import static com.android.server.utils.EventLogger.Event.ALOGE;
 import static com.android.server.utils.EventLogger.Event.ALOGI;
@@ -607,6 +608,7 @@
     };
 
     private final boolean mUseFixedVolume;
+    private final boolean mRingerModeAffectsAlarm;
     private final boolean mUseVolumeGroupAliases;
 
     // If absolute volume is supported in AVRCP device
@@ -1300,6 +1302,9 @@
         mUseFixedVolume = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_useFixedVolume);
 
+        mRingerModeAffectsAlarm = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_audio_ringer_mode_affects_alarm_stream);
+
         mRecordMonitor = new RecordingActivityMonitor(mContext);
         mRecordMonitor.registerRecordingCallback(mVoiceRecordingActivityMonitor, true);
 
@@ -7019,6 +7024,19 @@
             ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_DTMF);
         }
 
+        if (ringerModeAffectsAlarm()) {
+            if (mRingerModeAffectsAlarm) {
+                boolean muteAlarmWithRinger =
+                        mSettings.getGlobalInt(mContentResolver,
+                        Settings.Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE,
+                        /* def= */ 0) != 0;
+                if (muteAlarmWithRinger) {
+                    ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_ALARM);
+                } else {
+                    ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_ALARM);
+                }
+            }
+        }
         if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
             mSettings.putSystemIntForUser(mContentResolver,
                     Settings.System.MODE_RINGER_STREAMS_AFFECTED,
@@ -9678,6 +9696,8 @@
                     Settings.Global.ZEN_MODE), false, this);
             mContentResolver.registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.ZEN_MODE_CONFIG_ETAG), false, this);
+            mContentResolver.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE), false, this);
             mContentResolver.registerContentObserver(Settings.System.getUriFor(
                 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
             mContentResolver.registerContentObserver(Settings.Global.getUriFor(
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index f994c05..bcf27b4 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -26,9 +26,12 @@
 import android.view.Choreographer;
 import android.view.Display;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
 import com.android.server.display.utils.DebugUtils;
 
 import java.io.PrintWriter;
+import java.util.concurrent.Executor;
 
 /**
  * Controls the display power state.
@@ -75,10 +78,19 @@
 
     private Runnable mCleanListener;
 
+    private Executor mAsyncDestroyExecutor;
+
     private volatile boolean mStopped;
 
     DisplayPowerState(
             DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) {
+        this(blanker, colorFade, displayId, displayState, BackgroundThread.getExecutor());
+    }
+
+    @VisibleForTesting
+    DisplayPowerState(
+            DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState,
+            Executor asyncDestroyExecutor) {
         mHandler = new Handler(true /*async*/);
         mChoreographer = Choreographer.getInstance();
         mBlanker = blanker;
@@ -86,6 +98,7 @@
         mPhotonicModulator = new PhotonicModulator();
         mPhotonicModulator.start();
         mDisplayId = displayId;
+        mAsyncDestroyExecutor = asyncDestroyExecutor;
 
         // At boot time, we don't know the screen's brightness,
         // so prepare to set it to a known state when the state is next applied.
@@ -321,7 +334,7 @@
         mStopped = true;
         mPhotonicModulator.interrupt();
         if (mColorFade != null) {
-            mColorFade.destroy();
+            mAsyncDestroyExecutor.execute(mColorFade::destroy);
         }
         mCleanListener = null;
         mHandler.removeCallbacksAndMessages(null);
diff --git a/services/core/java/com/android/server/flags/OWNERS b/services/core/java/com/android/server/flags/OWNERS
index 535a750..60ceb12 100644
--- a/services/core/java/com/android/server/flags/OWNERS
+++ b/services/core/java/com/android/server/flags/OWNERS
@@ -1 +1,2 @@
-per-file pinner.aconfig = edgararriaga@google.com
\ No newline at end of file
+per-file pinner.aconfig = edgararriaga@google.com
+per-file compaction.aconfig = edgararriaga@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/flags/compaction.aconfig b/services/core/java/com/android/server/flags/compaction.aconfig
new file mode 100644
index 0000000..58cc560
--- /dev/null
+++ b/services/core/java/com/android/server/flags/compaction.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.flags"
+
+flag {
+    name: "disable_system_compaction"
+    namespace: "system_performance"
+    description: "This flag controls if all processes compaction should happen during idle maintenance."
+    bug: "314328789"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 773293f..a88d85e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -229,31 +229,6 @@
             }
         }
 
-        private static List<Pair<String, ArrayList<String>>> buildInputMethodsAndSubtypeList(
-                String enabledInputMethodsStr,
-                TextUtils.SimpleStringSplitter inputMethodSplitter,
-                TextUtils.SimpleStringSplitter subtypeSplitter) {
-            ArrayList<Pair<String, ArrayList<String>>> imsList = new ArrayList<>();
-            if (TextUtils.isEmpty(enabledInputMethodsStr)) {
-                return imsList;
-            }
-            inputMethodSplitter.setString(enabledInputMethodsStr);
-            while (inputMethodSplitter.hasNext()) {
-                String nextImsStr = inputMethodSplitter.next();
-                subtypeSplitter.setString(nextImsStr);
-                if (subtypeSplitter.hasNext()) {
-                    ArrayList<String> subtypeHashes = new ArrayList<>();
-                    // The first element is ime id.
-                    String imeId = subtypeSplitter.next();
-                    while (subtypeSplitter.hasNext()) {
-                        subtypeHashes.add(subtypeSplitter.next());
-                    }
-                    imsList.add(new Pair<>(imeId, subtypeHashes));
-                }
-            }
-            return imsList;
-        }
-
         InputMethodSettings(ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId,
                 boolean copyOnWrite) {
             mMethodMap = methodMap;
@@ -351,18 +326,30 @@
         }
 
         List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() {
-            return buildInputMethodsAndSubtypeList(getEnabledInputMethodsStr(),
-                    new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATOR),
-                    new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR));
-        }
-
-        List<String> getEnabledInputMethodNames() {
-            List<String> result = new ArrayList<>();
-            for (Pair<String, ArrayList<String>> pair :
-                    getEnabledInputMethodsAndSubtypeListLocked()) {
-                result.add(pair.first);
+            final String enabledInputMethodsStr = getEnabledInputMethodsStr();
+            final TextUtils.SimpleStringSplitter inputMethodSplitter =
+                    new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
+            final TextUtils.SimpleStringSplitter subtypeSplitter =
+                    new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);
+            final ArrayList<Pair<String, ArrayList<String>>> imsList = new ArrayList<>();
+            if (TextUtils.isEmpty(enabledInputMethodsStr)) {
+                return imsList;
             }
-            return result;
+            inputMethodSplitter.setString(enabledInputMethodsStr);
+            while (inputMethodSplitter.hasNext()) {
+                String nextImsStr = inputMethodSplitter.next();
+                subtypeSplitter.setString(nextImsStr);
+                if (subtypeSplitter.hasNext()) {
+                    ArrayList<String> subtypeHashes = new ArrayList<>();
+                    // The first element is ime id.
+                    String imeId = subtypeSplitter.next();
+                    while (subtypeSplitter.hasNext()) {
+                        subtypeHashes.add(subtypeSplitter.next());
+                    }
+                    imsList.add(new Pair<>(imeId, subtypeHashes));
+                }
+            }
+            return imsList;
         }
 
         void appendAndPutEnabledInputMethodLocked(String id) {
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
index 8504495..0eb9166 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
@@ -87,6 +87,8 @@
 
     @NonNull private final AudioDeviceCallback mAudioDeviceCallback = new AudioDeviceCallbackImpl();
 
+    @MediaRoute2Info.SuitabilityStatus private final int mBuiltInSpeakerSuitabilityStatus;
+
     @NonNull
     private final AudioManager.OnDevicesForAttributesChangedListener
             mOnDevicesForAttributesChangedListener = this::onDevicesForAttributesChangedListener;
@@ -113,6 +115,10 @@
         mHandler = new Handler(Objects.requireNonNull(looper));
         mStrategyForMedia = Objects.requireNonNull(strategyForMedia);
         mOnDeviceRouteChangedListener = Objects.requireNonNull(onDeviceRouteChangedListener);
+
+        mBuiltInSpeakerSuitabilityStatus =
+                DeviceRouteController.getBuiltInSpeakerSuitabilityStatus(mContext);
+
         mBluetoothRouteController =
                 new AudioPoliciesBluetoothRouteController(
                         mContext, btAdapter, this::rebuildAvailableRoutesAndNotify);
@@ -373,14 +379,19 @@
             // from getting an id using BluetoothRouteController#getRouteIdForBluetoothAddress.
             routeId = systemRouteInfo.mDefaultRouteId;
         }
-        return new MediaRoute2Info.Builder(routeId, humanReadableName)
+        MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(routeId, humanReadableName)
                 .setType(systemRouteInfo.mMediaRoute2InfoType)
                 .setAddress(address)
                 .setSystemRoute(true)
                 .addFeature(FEATURE_LIVE_AUDIO)
                 .addFeature(FEATURE_LOCAL_PLAYBACK)
-                .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
-                .build();
+                .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED);
+
+        if (systemRouteInfo.mMediaRoute2InfoType == MediaRoute2Info.TYPE_BUILTIN_SPEAKER) {
+            builder.setSuitabilityStatus(mBuiltInSpeakerSuitabilityStatus);
+        }
+
+        return builder.build();
     }
 
     /**
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index 9f175a9..8b62cc9 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -81,6 +81,30 @@
         }
     }
 
+    /** Returns device route availability status. */
+    @MediaRoute2Info.SuitabilityStatus
+    static int getBuiltInSpeakerSuitabilityStatus(@NonNull Context context) {
+        if (!Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+            // Route is always suitable if the flag is disabled.
+            return MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
+        }
+
+        int availabilityStatus =
+                context.getResources()
+                        .getInteger(
+                                com.android.internal.R.integer
+                                        .config_mediaRouter_builtInSpeakerSuitability);
+
+        switch (availabilityStatus) {
+            case MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER:
+            case MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER:
+            case MediaRoute2Info.SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER:
+                return availabilityStatus;
+            default:
+                return MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
+        }
+    }
+
     /** Returns the currently selected device (built-in or wired) route. */
     @NonNull
     MediaRoute2Info getSelectedRoute();
diff --git a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
index c0f2834..65b0ad0 100644
--- a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
@@ -72,6 +72,8 @@
     @NonNull
     private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver();
 
+    @MediaRoute2Info.SuitabilityStatus private final int mBuiltInSpeakerSuitabilityStatus;
+
     private int mDeviceVolume;
     private MediaRoute2Info mDeviceRoute;
 
@@ -90,6 +92,9 @@
         mAudioManager = audioManager;
         mAudioService = audioService;
 
+        mBuiltInSpeakerSuitabilityStatus =
+                DeviceRouteController.getBuiltInSpeakerSuitabilityStatus(mContext);
+
         AudioRoutesInfo newAudioRoutes = null;
         try {
             newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
@@ -165,19 +170,28 @@
         }
 
         synchronized (this) {
-            return new MediaRoute2Info.Builder(
-                    DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
-                    .setVolumeHandling(mAudioManager.isVolumeFixed()
-                            ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
-                            : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
-                    .setVolume(mDeviceVolume)
-                    .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
-                    .setType(type)
-                    .addFeature(FEATURE_LIVE_AUDIO)
-                    .addFeature(FEATURE_LIVE_VIDEO)
-                    .addFeature(FEATURE_LOCAL_PLAYBACK)
-                    .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
-                    .build();
+            MediaRoute2Info.Builder builder =
+                    new MediaRoute2Info.Builder(
+                                    DEVICE_ROUTE_ID,
+                                    mContext.getResources().getText(name).toString())
+                            .setVolumeHandling(
+                                    mAudioManager.isVolumeFixed()
+                                            ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
+                                            : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+                            .setVolume(mDeviceVolume)
+                            .setVolumeMax(
+                                    mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+                            .setType(type)
+                            .addFeature(FEATURE_LIVE_AUDIO)
+                            .addFeature(FEATURE_LIVE_VIDEO)
+                            .addFeature(FEATURE_LOCAL_PLAYBACK)
+                            .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED);
+
+            if (type == TYPE_BUILTIN_SPEAKER) {
+                builder.setSuitabilityStatus(mBuiltInSpeakerSuitabilityStatus);
+            }
+
+            return builder.build();
         }
     }
 
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 8149847..1bc2a5e 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -24,6 +24,7 @@
 import android.media.RouteDiscoveryPreference;
 import android.media.RoutingSessionInfo;
 import android.os.Bundle;
+import android.os.UserHandle;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -54,8 +55,15 @@
         mCallback = callback;
     }
 
-    public abstract void requestCreateSession(long requestId, String packageName, String routeId,
-            @Nullable Bundle sessionHints);
+    public abstract void requestCreateSession(
+            long requestId,
+            String packageName,
+            String routeId,
+            @Nullable Bundle sessionHints,
+            @RoutingSessionInfo.TransferReason int transferReason,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName);
+
     public abstract void releaseSession(long requestId, String sessionId);
 
     public abstract void updateDiscoveryPreference(
@@ -63,7 +71,14 @@
 
     public abstract void selectRoute(long requestId, String sessionId, String routeId);
     public abstract void deselectRoute(long requestId, String sessionId, String routeId);
-    public abstract void transferToRoute(long requestId, String sessionId, String routeId);
+
+    public abstract void transferToRoute(
+            long requestId,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName,
+            String sessionId,
+            String routeId,
+            @RoutingSessionInfo.TransferReason int transferReason);
 
     public abstract void setRouteVolume(long requestId, String routeId, int volume);
     public abstract void setSessionVolume(long requestId, String sessionId, int volume);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 330818e..ae889d8 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -98,8 +98,14 @@
     }
 
     @Override
-    public void requestCreateSession(long requestId, String packageName, String routeId,
-            Bundle sessionHints) {
+    public void requestCreateSession(
+            long requestId,
+            String packageName,
+            String routeId,
+            Bundle sessionHints,
+            @RoutingSessionInfo.TransferReason int transferReason,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName) {
         if (mConnectionReady) {
             mActiveConnection.requestCreateSession(requestId, packageName, routeId, sessionHints);
             updateBinding();
@@ -141,7 +147,13 @@
     }
 
     @Override
-    public void transferToRoute(long requestId, String sessionId, String routeId) {
+    public void transferToRoute(
+            long requestId,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName,
+            String sessionId,
+            String routeId,
+            @RoutingSessionInfo.TransferReason int transferReason) {
         if (mConnectionReady) {
             mActiveConnection.transferToRoute(requestId, sessionId, routeId);
         }
@@ -649,6 +661,14 @@
                                     + "Disallowed route: "
                                     + route);
                 }
+
+                if (route.getSuitabilityStatus()
+                        == MediaRoute2Info.SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER) {
+                    throw new SecurityException(
+                            "Only the system is allowed to set not suitable for transfer status. "
+                                    + "Disallowed route: "
+                                    + route);
+                }
             }
 
             Connection connection = mConnectionRef.get();
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 5e18727..38f0df4 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -337,18 +337,47 @@
         }
     }
 
-    public void requestCreateSessionWithRouter2(@NonNull IMediaRouter2 router, int requestId,
-            long managerRequestId, @NonNull RoutingSessionInfo oldSession,
-            @NonNull MediaRoute2Info route, Bundle sessionHints) {
+    public void requestCreateSessionWithRouter2(
+            @NonNull IMediaRouter2 router,
+            int requestId,
+            long managerRequestId,
+            @NonNull RoutingSessionInfo oldSession,
+            @NonNull MediaRoute2Info route,
+            Bundle sessionHints,
+            @Nullable UserHandle transferInitiatorUserHandle,
+            @Nullable String transferInitiatorPackageName) {
         Objects.requireNonNull(router, "router must not be null");
         Objects.requireNonNull(oldSession, "oldSession must not be null");
         Objects.requireNonNull(route, "route must not be null");
 
+        synchronized (mLock) {
+            if (managerRequestId == MediaRoute2ProviderService.REQUEST_ID_NONE
+                    || transferInitiatorUserHandle == null
+                    || transferInitiatorPackageName == null) {
+                final IBinder binder = router.asBinder();
+                final RouterRecord routerRecord = mAllRouterRecords.get(binder);
+
+                transferInitiatorUserHandle = Binder.getCallingUserHandle();
+                if (routerRecord != null) {
+                    transferInitiatorPackageName = routerRecord.mPackageName;
+                } else {
+                    transferInitiatorPackageName = mContext.getPackageName();
+                }
+            }
+        }
+
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                requestCreateSessionWithRouter2Locked(requestId, managerRequestId,
-                        router, oldSession, route, sessionHints);
+                requestCreateSessionWithRouter2Locked(
+                        requestId,
+                        managerRequestId,
+                        transferInitiatorUserHandle,
+                        transferInitiatorPackageName,
+                        router,
+                        oldSession,
+                        route,
+                        sessionHints);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -399,10 +428,11 @@
             throw new IllegalArgumentException("uniqueSessionId must not be empty");
         }
 
+        UserHandle userHandle = Binder.getCallingUserHandle();
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                transferToRouteWithRouter2Locked(router, uniqueSessionId, route);
+                transferToRouteWithRouter2Locked(router, userHandle, uniqueSessionId, route);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -588,16 +618,28 @@
         }
     }
 
-    public void requestCreateSessionWithManager(@NonNull IMediaRouter2Manager manager,
-            int requestId, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) {
+    public void requestCreateSessionWithManager(
+            @NonNull IMediaRouter2Manager manager,
+            int requestId,
+            @NonNull RoutingSessionInfo oldSession,
+            @NonNull MediaRoute2Info route,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName) {
         Objects.requireNonNull(manager, "manager must not be null");
         Objects.requireNonNull(oldSession, "oldSession must not be null");
         Objects.requireNonNull(route, "route must not be null");
+        Objects.requireNonNull(transferInitiatorUserHandle);
 
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                requestCreateSessionWithManagerLocked(requestId, manager, oldSession, route);
+                requestCreateSessionWithManagerLocked(
+                        requestId,
+                        manager,
+                        oldSession,
+                        route,
+                        transferInitiatorUserHandle,
+                        transferInitiatorPackageName);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -640,18 +682,32 @@
         }
     }
 
-    public void transferToRouteWithManager(@NonNull IMediaRouter2Manager manager, int requestId,
-            @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+    public void transferToRouteWithManager(
+            @NonNull IMediaRouter2Manager manager,
+            int requestId,
+            @NonNull String uniqueSessionId,
+            @NonNull MediaRoute2Info route,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName) {
         Objects.requireNonNull(manager, "manager must not be null");
         if (TextUtils.isEmpty(uniqueSessionId)) {
             throw new IllegalArgumentException("uniqueSessionId must not be empty");
         }
         Objects.requireNonNull(route, "route must not be null");
+        Objects.requireNonNull(transferInitiatorUserHandle);
+        Objects.requireNonNull(transferInitiatorPackageName);
 
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                transferToRouteWithManagerLocked(requestId, manager, uniqueSessionId, route);
+                transferToRouteWithManagerLocked(
+                        requestId,
+                        manager,
+                        uniqueSessionId,
+                        route,
+                        RoutingSessionInfo.TRANSFER_REASON_SYSTEM_REQUEST,
+                        transferInitiatorUserHandle,
+                        transferInitiatorPackageName);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -1038,9 +1094,15 @@
     }
 
     @GuardedBy("mLock")
-    private void requestCreateSessionWithRouter2Locked(int requestId, long managerRequestId,
-            @NonNull IMediaRouter2 router, @NonNull RoutingSessionInfo oldSession,
-            @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) {
+    private void requestCreateSessionWithRouter2Locked(
+            int requestId,
+            long managerRequestId,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName,
+            @NonNull IMediaRouter2 router,
+            @NonNull RoutingSessionInfo oldSession,
+            @NonNull MediaRoute2Info route,
+            @Nullable Bundle sessionHints) {
         final IBinder binder = router.asBinder();
         final RouterRecord routerRecord = mAllRouterRecords.get(binder);
 
@@ -1114,9 +1176,16 @@
 
         long uniqueRequestId = toUniqueRequestId(routerRecord.mRouterId, requestId);
         routerRecord.mUserRecord.mHandler.sendMessage(
-                obtainMessage(UserHandler::requestCreateSessionWithRouter2OnHandler,
+                obtainMessage(
+                        UserHandler::requestCreateSessionWithRouter2OnHandler,
                         routerRecord.mUserRecord.mHandler,
-                        uniqueRequestId, managerRequestId, routerRecord, oldSession, route,
+                        uniqueRequestId,
+                        managerRequestId,
+                        transferInitiatorUserHandle,
+                        transferInitiatorPackageName,
+                        routerRecord,
+                        oldSession,
+                        route,
                         sessionHints));
     }
 
@@ -1165,8 +1234,11 @@
     }
 
     @GuardedBy("mLock")
-    private void transferToRouteWithRouter2Locked(@NonNull IMediaRouter2 router,
-            @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+    private void transferToRouteWithRouter2Locked(
+            @NonNull IMediaRouter2 router,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String uniqueSessionId,
+            @NonNull MediaRoute2Info route) {
         final IBinder binder = router.asBinder();
         final RouterRecord routerRecord = mAllRouterRecords.get(binder);
 
@@ -1191,9 +1263,16 @@
                             routerRecord, toOriginalRequestId(DUMMY_REQUEST_ID)));
         } else {
             routerRecord.mUserRecord.mHandler.sendMessage(
-                    obtainMessage(UserHandler::transferToRouteOnHandler,
+                    obtainMessage(
+                            UserHandler::transferToRouteOnHandler,
                             routerRecord.mUserRecord.mHandler,
-                            DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route));
+                            DUMMY_REQUEST_ID,
+                            transferInitiatorUserHandle,
+                            routerRecord.mPackageName,
+                            routerRecord,
+                            uniqueSessionId,
+                            route,
+                            RoutingSessionInfo.TRANSFER_REASON_APP));
         }
     }
 
@@ -1416,9 +1495,13 @@
     }
 
     @GuardedBy("mLock")
-    private void requestCreateSessionWithManagerLocked(int requestId,
-            @NonNull IMediaRouter2Manager manager, @NonNull RoutingSessionInfo oldSession,
-            @NonNull MediaRoute2Info route) {
+    private void requestCreateSessionWithManagerLocked(
+            int requestId,
+            @NonNull IMediaRouter2Manager manager,
+            @NonNull RoutingSessionInfo oldSession,
+            @NonNull MediaRoute2Info route,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName) {
         ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder());
         if (managerRecord == null) {
             return;
@@ -1464,9 +1547,16 @@
         // Before requesting to the provider, get session hints from the media router.
         // As a return, media router will request to create a session.
         routerRecord.mUserRecord.mHandler.sendMessage(
-                obtainMessage(UserHandler::requestRouterCreateSessionOnHandler,
+                obtainMessage(
+                        UserHandler::requestRouterCreateSessionOnHandler,
                         routerRecord.mUserRecord.mHandler,
-                        uniqueRequestId, routerRecord, managerRecord, oldSession, route));
+                        uniqueRequestId,
+                        routerRecord,
+                        managerRecord,
+                        oldSession,
+                        route,
+                        transferInitiatorUserHandle,
+                        transferInitiatorPackageName));
     }
 
     @GuardedBy("mLock")
@@ -1521,9 +1611,14 @@
     }
 
     @GuardedBy("mLock")
-    private void transferToRouteWithManagerLocked(int requestId,
+    private void transferToRouteWithManagerLocked(
+            int requestId,
             @NonNull IMediaRouter2Manager manager,
-            @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+            @NonNull String uniqueSessionId,
+            @NonNull MediaRoute2Info route,
+            @RoutingSessionInfo.TransferReason int transferReason,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName) {
         final IBinder binder = manager.asBinder();
         ManagerRecord managerRecord = mAllManagerRecords.get(binder);
 
@@ -1541,9 +1636,16 @@
 
         long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
         managerRecord.mUserRecord.mHandler.sendMessage(
-                obtainMessage(UserHandler::transferToRouteOnHandler,
+                obtainMessage(
+                        UserHandler::transferToRouteOnHandler,
                         managerRecord.mUserRecord.mHandler,
-                        uniqueRequestId, routerRecord, uniqueSessionId, route));
+                        uniqueRequestId,
+                        transferInitiatorUserHandle,
+                        transferInitiatorPackageName,
+                        routerRecord,
+                        uniqueSessionId,
+                        route,
+                        transferReason));
     }
 
     @GuardedBy("mLock")
@@ -1850,6 +1952,19 @@
             }
         }
 
+        public void notifySessionCreated(int requestId, @NonNull RoutingSessionInfo sessionInfo) {
+            try {
+                mRouter.notifySessionCreated(
+                        requestId, maybeClearTransferInitiatorIdentity(sessionInfo));
+            } catch (RemoteException ex) {
+                Slog.w(
+                        TAG,
+                        "Failed to notify router of the session creation."
+                                + " Router probably died.",
+                        ex);
+            }
+        }
+
         /**
          * Sends the corresponding router an update for the given session.
          *
@@ -1857,12 +1972,27 @@
          */
         public void notifySessionInfoChanged(RoutingSessionInfo sessionInfo) {
             try {
-                mRouter.notifySessionInfoChanged(sessionInfo);
+                mRouter.notifySessionInfoChanged(maybeClearTransferInitiatorIdentity(sessionInfo));
             } catch (RemoteException ex) {
                 Slog.w(TAG, "Failed to notify session info changed. Router probably died.", ex);
             }
         }
 
+        private RoutingSessionInfo maybeClearTransferInitiatorIdentity(
+                @NonNull RoutingSessionInfo sessionInfo) {
+            UserHandle transferInitiatorUserHandle = sessionInfo.getTransferInitiatorUserHandle();
+            String transferInitiatorPackageName = sessionInfo.getTransferInitiatorPackageName();
+
+            if (!Objects.equals(UserHandle.of(mUserRecord.mUserId), transferInitiatorUserHandle)
+                    || !Objects.equals(mPackageName, transferInitiatorPackageName)) {
+                return new RoutingSessionInfo.Builder(sessionInfo)
+                        .setTransferInitiator(null, null)
+                        .build();
+            }
+
+            return sessionInfo;
+        }
+
         /**
          * Returns a filtered copy of {@code routes} that contains only the routes that are {@link
          * MediaRoute2Info#isVisibleTo visible} to the router corresponding to this record.
@@ -2307,9 +2437,14 @@
             return -1;
         }
 
-        private void requestRouterCreateSessionOnHandler(long uniqueRequestId,
-                @NonNull RouterRecord routerRecord, @NonNull ManagerRecord managerRecord,
-                @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) {
+        private void requestRouterCreateSessionOnHandler(
+                long uniqueRequestId,
+                @NonNull RouterRecord routerRecord,
+                @NonNull ManagerRecord managerRecord,
+                @NonNull RoutingSessionInfo oldSession,
+                @NonNull MediaRoute2Info route,
+                @NonNull UserHandle transferInitiatorUserHandle,
+                @NonNull String transferInitiatorPackageName) {
             try {
                 if (route.isSystemRoute() && !routerRecord.hasSystemRoutingPermission()) {
                     // The router lacks permission to modify system routing, so we hide system
@@ -2317,7 +2452,11 @@
                     route = mSystemProvider.getDefaultRoute();
                 }
                 routerRecord.mRouter.requestCreateSessionByManager(
-                        uniqueRequestId, oldSession, route);
+                        uniqueRequestId,
+                        oldSession,
+                        route,
+                        transferInitiatorUserHandle,
+                        transferInitiatorPackageName);
             } catch (RemoteException ex) {
                 Slog.w(TAG, "getSessionHintsForCreatingSessionOnHandler: "
                         + "Failed to request. Router probably died.", ex);
@@ -2326,10 +2465,15 @@
             }
         }
 
-        private void requestCreateSessionWithRouter2OnHandler(long uniqueRequestId,
-                long managerRequestId, @NonNull RouterRecord routerRecord,
+        private void requestCreateSessionWithRouter2OnHandler(
+                long uniqueRequestId,
+                long managerRequestId,
+                @NonNull UserHandle transferInitiatorUserHandle,
+                @NonNull String transferInitiatorPackageName,
+                @NonNull RouterRecord routerRecord,
                 @NonNull RoutingSessionInfo oldSession,
-                @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) {
+                @NonNull MediaRoute2Info route,
+                @Nullable Bundle sessionHints) {
 
             final MediaRoute2Provider provider = findProvider(route.getProviderId());
             if (provider == null) {
@@ -2345,8 +2489,19 @@
                             managerRequestId, oldSession, route);
             mSessionCreationRequests.add(request);
 
-            provider.requestCreateSession(uniqueRequestId, routerRecord.mPackageName,
-                    route.getOriginalId(), sessionHints);
+            int transferReason = RoutingSessionInfo.TRANSFER_REASON_APP;
+            if (managerRequestId != MediaRoute2ProviderService.REQUEST_ID_NONE) {
+                transferReason = RoutingSessionInfo.TRANSFER_REASON_SYSTEM_REQUEST;
+            }
+
+            provider.requestCreateSession(
+                    uniqueRequestId,
+                    routerRecord.mPackageName,
+                    route.getOriginalId(),
+                    sessionHints,
+                    transferReason,
+                    transferInitiatorUserHandle,
+                    transferInitiatorPackageName);
         }
 
         // routerRecord can be null if the session is system's or RCN.
@@ -2386,9 +2541,14 @@
         }
 
         // routerRecord can be null if the session is system's or RCN.
-        private void transferToRouteOnHandler(long uniqueRequestId,
+        private void transferToRouteOnHandler(
+                long uniqueRequestId,
+                @NonNull UserHandle transferInitiatorUserHandle,
+                @NonNull String transferInitiatorPackageName,
                 @Nullable RouterRecord routerRecord,
-                @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+                @NonNull String uniqueSessionId,
+                @NonNull MediaRoute2Info route,
+                @RoutingSessionInfo.TransferReason int transferReason) {
             if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route,
                     "transferring to")) {
                 return;
@@ -2399,8 +2559,13 @@
             if (provider == null) {
                 return;
             }
-            provider.transferToRoute(uniqueRequestId, getOriginalId(uniqueSessionId),
-                    route.getOriginalId());
+            provider.transferToRoute(
+                    uniqueRequestId,
+                    transferInitiatorUserHandle,
+                    transferInitiatorPackageName,
+                    getOriginalId(uniqueSessionId),
+                    route.getOriginalId(),
+                    transferReason);
         }
 
         // routerRecord is null if and only if the session is created without the request, which
@@ -2535,6 +2700,7 @@
                 // session info from them.
                 sessionInfo = mSystemProvider.getDefaultSessionInfo();
             }
+            // TODO: b/279555229 - replace with matchingRequest.mRouterRecord.notifySessionCreated.
             notifySessionCreatedToRouter(
                     matchingRequest.mRouterRecord,
                     toOriginalRequestId(uniqueRequestId),
@@ -2648,12 +2814,7 @@
 
         private void notifySessionCreatedToRouter(@NonNull RouterRecord routerRecord,
                 int requestId, @NonNull RoutingSessionInfo sessionInfo) {
-            try {
-                routerRecord.mRouter.notifySessionCreated(requestId, sessionInfo);
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to notify router of the session creation."
-                        + " Router probably died.", ex);
-            }
+            routerRecord.notifySessionCreated(requestId, sessionInfo);
         }
 
         private void notifySessionCreationFailedToRouter(@NonNull RouterRecord routerRecord,
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index e562b3f..7dd1314 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -461,11 +461,24 @@
 
     // Binder call
     @Override
-    public void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId,
-            long managerRequestId, RoutingSessionInfo oldSession,
-            MediaRoute2Info route, Bundle sessionHints) {
-        mService2.requestCreateSessionWithRouter2(router, requestId, managerRequestId,
-                oldSession, route, sessionHints);
+    public void requestCreateSessionWithRouter2(
+            IMediaRouter2 router,
+            int requestId,
+            long managerRequestId,
+            RoutingSessionInfo oldSession,
+            MediaRoute2Info route,
+            Bundle sessionHints,
+            @Nullable UserHandle transferInitiatorUserHandle,
+            @Nullable String transferInitiatorPackageName) {
+        mService2.requestCreateSessionWithRouter2(
+                router,
+                requestId,
+                managerRequestId,
+                oldSession,
+                route,
+                sessionHints,
+                transferInitiatorUserHandle,
+                transferInitiatorPackageName);
     }
 
     // Binder call
@@ -580,9 +593,20 @@
 
     // Binder call
     @Override
-    public void requestCreateSessionWithManager(IMediaRouter2Manager manager,
-            int requestId, RoutingSessionInfo oldSession, MediaRoute2Info route) {
-        mService2.requestCreateSessionWithManager(manager, requestId, oldSession, route);
+    public void requestCreateSessionWithManager(
+            IMediaRouter2Manager manager,
+            int requestId,
+            RoutingSessionInfo oldSession,
+            MediaRoute2Info route,
+            UserHandle transferInitiatorUserHandle,
+            String transferInitiatorPackageName) {
+        mService2.requestCreateSessionWithManager(
+                manager,
+                requestId,
+                oldSession,
+                route,
+                transferInitiatorUserHandle,
+                transferInitiatorPackageName);
     }
 
     // Binder call
@@ -601,9 +625,20 @@
 
     // Binder call
     @Override
-    public void transferToRouteWithManager(IMediaRouter2Manager manager, int requestId,
-            String sessionId, MediaRoute2Info route) {
-        mService2.transferToRouteWithManager(manager, requestId, sessionId, route);
+    public void transferToRouteWithManager(
+            IMediaRouter2Manager manager,
+            int requestId,
+            String sessionId,
+            MediaRoute2Info route,
+            UserHandle transferInitiatorUserHandle,
+            String transferInitiatorPackageName) {
+        mService2.transferToRouteWithManager(
+                manager,
+                requestId,
+                sessionId,
+                route,
+                transferInitiatorUserHandle,
+                transferInitiatorPackageName);
     }
 
     // Binder call
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 9d151c2..f7210dd 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -16,6 +16,7 @@
 
 package com.android.server.media;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -26,6 +27,7 @@
 import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
 import android.media.MediaRoute2ProviderService;
+import android.media.MediaRouter2Utils;
 import android.media.RouteDiscoveryPreference;
 import android.media.RoutingSessionInfo;
 import android.os.Bundle;
@@ -39,6 +41,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.media.flags.Flags;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -79,6 +82,10 @@
     @GuardedBy("mRequestLock")
     private volatile SessionCreationRequest mPendingSessionCreationRequest;
 
+    private final Object mTransferLock = new Object();
+    @GuardedBy("mTransferLock")
+    @Nullable private volatile SessionCreationRequest mPendingTransferRequest;
+
     SystemMediaRoute2Provider(Context context, UserHandle user) {
         super(COMPONENT_NAME);
         mIsSystemRouteProvider = true;
@@ -146,17 +153,30 @@
     }
 
     @Override
-    public void requestCreateSession(long requestId, String packageName, String routeId,
-            Bundle sessionHints) {
+    public void requestCreateSession(
+            long requestId,
+            String packageName,
+            String routeId,
+            Bundle sessionHints,
+            @RoutingSessionInfo.TransferReason int transferReason,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName) {
         // Assume a router without MODIFY_AUDIO_ROUTING permission can't request with
         // a route ID different from the default route ID. The service should've filtered.
         if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
             mCallback.onSessionCreated(this, requestId, mDefaultSessionInfo);
             return;
         }
-        if (TextUtils.equals(routeId, mSelectedRouteId)) {
-            mCallback.onSessionCreated(this, requestId, mSessionInfos.get(0));
-            return;
+
+        if (!Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+            if (TextUtils.equals(routeId, mSelectedRouteId)) {
+                RoutingSessionInfo currentSessionInfo;
+                synchronized (mLock) {
+                    currentSessionInfo = mSessionInfos.get(0);
+                }
+                mCallback.onSessionCreated(this, requestId, currentSessionInfo);
+                return;
+            }
         }
 
         synchronized (mRequestLock) {
@@ -165,10 +185,23 @@
                 mCallback.onRequestFailed(this, mPendingSessionCreationRequest.mRequestId,
                         MediaRoute2ProviderService.REASON_UNKNOWN_ERROR);
             }
-            mPendingSessionCreationRequest = new SessionCreationRequest(requestId, routeId);
+            mPendingSessionCreationRequest =
+                    new SessionCreationRequest(
+                            requestId,
+                            routeId,
+                            RoutingSessionInfo.TRANSFER_REASON_FALLBACK,
+                            transferInitiatorUserHandle,
+                            transferInitiatorPackageName);
         }
 
-        transferToRoute(requestId, SYSTEM_SESSION_ID, routeId);
+        // Only unprivileged routers call this method, therefore we use TRANSFER_REASON_APP.
+        transferToRoute(
+                requestId,
+                transferInitiatorUserHandle,
+                transferInitiatorPackageName,
+                SYSTEM_SESSION_ID,
+                routeId,
+                transferReason);
     }
 
     @Override
@@ -193,12 +226,31 @@
     }
 
     @Override
-    public void transferToRoute(long requestId, String sessionId, String routeId) {
+    public void transferToRoute(
+            long requestId,
+            @NonNull UserHandle transferInitiatorUserHandle,
+            @NonNull String transferInitiatorPackageName,
+            String sessionId,
+            String routeId,
+            @RoutingSessionInfo.TransferReason int transferReason) {
         if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
             // The currently selected route is the default route.
             Log.w(TAG, "Ignoring transfer to " + MediaRoute2Info.ROUTE_ID_DEFAULT);
             return;
         }
+
+        if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+            synchronized (mTransferLock) {
+                mPendingTransferRequest =
+                        new SessionCreationRequest(
+                                requestId,
+                                routeId,
+                                transferReason,
+                                transferInitiatorUserHandle,
+                                transferInitiatorPackageName);
+            }
+        }
+
         MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
         boolean isAvailableDeviceRoute =
                 mDeviceRouteController.getAvailableRoutes().stream()
@@ -218,6 +270,11 @@
             mDeviceRouteController.transferTo(null);
             mBluetoothRouteController.transferTo(routeId);
         }
+
+        if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()
+                && updateSessionInfosIfNeeded()) {
+            notifySessionInfoUpdated();
+        }
     }
 
     @Override
@@ -322,9 +379,11 @@
             MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
             MediaRoute2Info selectedRoute = selectedDeviceRoute;
             MediaRoute2Info selectedBtRoute = mBluetoothRouteController.getSelectedRoute();
+            List<String> transferableRoutes = new ArrayList<>();
+
             if (selectedBtRoute != null) {
                 selectedRoute = selectedBtRoute;
-                builder.addTransferableRoute(selectedDeviceRoute.getId());
+                transferableRoutes.add(selectedDeviceRoute.getId());
             }
             mSelectedRouteId = selectedRoute.getId();
             mDefaultRoute =
@@ -337,12 +396,54 @@
                 for (MediaRoute2Info route : mDeviceRouteController.getAvailableRoutes()) {
                     String routeId = route.getId();
                     if (!mSelectedRouteId.equals(routeId)) {
-                        builder.addTransferableRoute(routeId);
+                        transferableRoutes.add(routeId);
                     }
                 }
             }
             for (MediaRoute2Info route : mBluetoothRouteController.getTransferableRoutes()) {
-                builder.addTransferableRoute(route.getId());
+                transferableRoutes.add(route.getId());
+            }
+
+            for (String route : transferableRoutes) {
+                builder.addTransferableRoute(route);
+            }
+
+            if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+                int transferReason = RoutingSessionInfo.TRANSFER_REASON_FALLBACK;
+                UserHandle transferInitiatorUserHandle = null;
+                String transferInitiatorPackageName = null;
+
+                if (oldSessionInfo != null
+                        && containsSelectedRouteWithId(oldSessionInfo, selectedRoute.getId())) {
+                    transferReason = oldSessionInfo.getTransferReason();
+                    transferInitiatorUserHandle = oldSessionInfo.getTransferInitiatorUserHandle();
+                    transferInitiatorPackageName = oldSessionInfo.getTransferInitiatorPackageName();
+                }
+
+                synchronized (mTransferLock) {
+                    if (mPendingTransferRequest != null) {
+                        boolean isTransferringToTheSelectedRoute =
+                                mPendingTransferRequest.isTargetRoute(selectedRoute);
+                        boolean canBePotentiallyTransferred =
+                                mPendingTransferRequest.isInsideOfRoutesList(transferableRoutes);
+
+                        if (isTransferringToTheSelectedRoute) {
+                            transferReason = mPendingTransferRequest.mTransferReason;
+                            transferInitiatorUserHandle =
+                                    mPendingTransferRequest.mTransferInitiatorUserHandle;
+                            transferInitiatorPackageName =
+                                    mPendingTransferRequest.mTransferInitiatorPackageName;
+
+                            mPendingTransferRequest = null;
+                        } else if (!canBePotentiallyTransferred) {
+                            mPendingTransferRequest = null;
+                        }
+                    }
+                }
+
+                builder.setTransferReason(transferReason)
+                        .setTransferInitiator(
+                                transferInitiatorUserHandle, transferInitiatorPackageName);
             }
 
             RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
@@ -424,6 +525,22 @@
         return false;
     }
 
+    private boolean containsSelectedRouteWithId(
+            @Nullable RoutingSessionInfo sessionInfo, @NonNull String selectedRouteId) {
+        if (sessionInfo == null) {
+            return false;
+        }
+
+        List<String> selectedRoutes = sessionInfo.getSelectedRoutes();
+
+        if (selectedRoutes.size() != 1) {
+            throw new IllegalStateException("Selected routes list should contain only 1 route id.");
+        }
+
+        String oldSelectedRouteId = MediaRouter2Utils.getOriginalId(selectedRoutes.get(0));
+        return oldSelectedRouteId != null && oldSelectedRouteId.equals(selectedRouteId);
+    }
+
     void publishProviderState() {
         updateProviderState();
         notifyProviderState();
@@ -452,12 +569,47 @@
     }
 
     private static class SessionCreationRequest {
-        final long mRequestId;
-        final String mRouteId;
+        private final long mRequestId;
+        @NonNull private final String mRouteId;
 
-        SessionCreationRequest(long requestId, String routeId) {
-            this.mRequestId = requestId;
-            this.mRouteId = routeId;
+        @RoutingSessionInfo.TransferReason private final int mTransferReason;
+
+        @NonNull private final UserHandle mTransferInitiatorUserHandle;
+        @NonNull private final String mTransferInitiatorPackageName;
+
+        SessionCreationRequest(
+                long requestId,
+                @NonNull String routeId,
+                @RoutingSessionInfo.TransferReason int transferReason,
+                @NonNull UserHandle transferInitiatorUserHandle,
+                @NonNull String transferInitiatorPackageName) {
+            mRequestId = requestId;
+            mRouteId = routeId;
+            mTransferReason = transferReason;
+            mTransferInitiatorUserHandle = transferInitiatorUserHandle;
+            mTransferInitiatorPackageName = transferInitiatorPackageName;
+        }
+
+        private boolean isTargetRoute(@Nullable MediaRoute2Info route2Info) {
+            if (route2Info == null) {
+                return false;
+            }
+
+            return isTargetRoute(route2Info.getId());
+        }
+
+        private boolean isTargetRoute(@Nullable String routeId) {
+            return mRouteId.equals(routeId);
+        }
+
+        private boolean isInsideOfRoutesList(@NonNull List<String> routesList) {
+            for (String routeId : routesList) {
+                if (isTargetRoute(routeId)) {
+                    return true;
+                }
+            }
+
+            return false;
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 135a467..13bf336c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5617,7 +5617,8 @@
             return !isCompatChangeEnabled
                     || isCallerSystemOrSystemUi()
                     || hasCompanionDevice(callingPkg, UserHandle.getUserId(callingUid),
-                            AssociationRequest.DEVICE_PROFILE_WATCH);
+                            Set.of(AssociationRequest.DEVICE_PROFILE_WATCH,
+                                    AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION));
         }
 
         private void enforcePolicyAccess(String pkg, String method) {
@@ -10753,6 +10754,14 @@
             final String key = record.getSbn().getKey();
             final NotificationListenerService.Ranking ranking =
                     new NotificationListenerService.Ranking();
+            ArrayList<Notification.Action> smartActions = record.getSystemGeneratedSmartActions();
+            ArrayList<CharSequence> smartReplies = record.getSmartReplies();
+            if (redactSensitiveNotificationsFromUntrustedListeners()
+                    && !mListeners.isUidTrusted(info.uid)
+                    && mListeners.hasSensitiveContent(record)) {
+                smartActions = null;
+                smartReplies = null;
+            }
             ranking.populate(
                     key,
                     rankings.size(),
@@ -10770,8 +10779,8 @@
                     record.isHidden(),
                     record.getLastAudiblyAlertedMs(),
                     record.getSound() != null || record.getVibration() != null,
-                    record.getSystemGeneratedSmartActions(),
-                    record.getSmartReplies(),
+                    smartActions,
+                    smartReplies,
                     record.canBubble(),
                     record.isTextChanged(),
                     record.isConversation(),
@@ -10800,7 +10809,7 @@
     }
 
     private boolean hasCompanionDevice(String pkg, @UserIdInt int userId,
-            @Nullable @AssociationRequest.DeviceProfile String withDeviceProfile) {
+            @Nullable Set</* @AssociationRequest.DeviceProfile */ String> withDeviceProfiles) {
         if (mCompanionManager == null) {
             mCompanionManager = getCompanionManager();
         }
@@ -10812,7 +10821,7 @@
         try {
             List<AssociationInfo> associations = mCompanionManager.getAssociations(pkg, userId);
             for (AssociationInfo association : associations) {
-                if (withDeviceProfile == null || withDeviceProfile.equals(
+                if (withDeviceProfiles == null || withDeviceProfiles.contains(
                         association.getDeviceProfile())) {
                     return true;
                 }
@@ -11521,20 +11530,16 @@
             super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled, userSet);
             String pkgName = getPackageName(pkgOrComponent);
             if (redactSensitiveNotificationsFromUntrustedListeners()) {
-                try {
-                    int uid = mPackageManagerClient.getPackageUidAsUser(pkgName, userId);
-                    if (!enabled) {
-                        synchronized (mTrustedListenerUids) {
-                            mTrustedListenerUids.remove(uid);
-                        }
+                int uid = mPackageManagerInternal.getPackageUid(pkgName, 0, userId);
+                if (!enabled && uid >= 0) {
+                    synchronized (mTrustedListenerUids) {
+                        mTrustedListenerUids.remove(uid);
                     }
-                    if (enabled && isAppTrustedNotificationListenerService(uid, pkgName)) {
-                        synchronized (mTrustedListenerUids) {
-                            mTrustedListenerUids.add(uid);
-                        }
+                }
+                if (enabled && uid >= 0 && isAppTrustedNotificationListenerService(uid, pkgName)) {
+                    synchronized (mTrustedListenerUids) {
+                        mTrustedListenerUids.add(uid);
                     }
-                } catch (NameNotFoundException e) {
-                    Slog.e(TAG, "PackageManager could not find package " + pkgName, e);
                 }
             }
 
@@ -11954,8 +11959,10 @@
 
                 for (final ManagedServiceInfo info : getServices()) {
                     boolean isTrusted = isUidTrusted(info.uid);
-                    boolean sendRedacted = isNewSensitive && !isTrusted;
-                    boolean sendOldRedacted = isOldSensitive && !isTrusted;
+                    boolean sendRedacted = redactSensitiveNotificationsFromUntrustedListeners()
+                            && isNewSensitive && !isTrusted;
+                    boolean sendOldRedacted = redactSensitiveNotificationsFromUntrustedListeners()
+                            && isOldSensitive && !isTrusted;
                     boolean sbnVisible = isVisibleToListener(sbn, r.getNotificationType(), info);
                     boolean oldSbnVisible = (oldSbn != null)
                             && isVisibleToListener(oldSbn, old.getNotificationType(), info);
@@ -12054,7 +12061,7 @@
 
         StatusBarNotification redactStatusBarNotification(StatusBarNotification sbn) {
             if (!redactSensitiveNotificationsFromUntrustedListeners()) {
-                return sbn;
+                throw new RuntimeException("redactStatusBarNotification called while flag is off");
             }
 
             ApplicationInfo appInfo = sbn.getNotification().extras.getParcelable(
@@ -12226,6 +12233,7 @@
         public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) {
             boolean isHiddenRankingUpdate = changedHiddenNotifications != null
                     && changedHiddenNotifications.size() > 0;
+
             // TODO (b/73052211): if the ranking update changed the notification type,
             // cancel notifications for NLSes that can't see them anymore
             for (final ManagedServiceInfo serviceInfo : getServices()) {
@@ -12249,7 +12257,6 @@
                 if (notifyThisListener || !isHiddenRankingUpdate) {
                     final NotificationRankingUpdate update = makeRankingUpdateLocked(
                             serviceInfo);
-
                     mHandler.post(() -> notifyRankingUpdate(serviceInfo, update));
                 }
             }
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 8b38f94..b638d30 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -673,6 +673,9 @@
                 if (pkgSetting == null || pkgSetting.getPkg() == null) {
                     return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender);
                 }
+                if (instantApp && (pkgSetting.isSystem() || pkgSetting.isUpdatedSystemApp())) {
+                    return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender);
+                }
                 if (!snapshot.canViewInstantApps(callingUid, UserHandle.getUserId(callingUid))) {
                     // only allow the existing package to be used if it's installed as a full
                     // application for at least one user
@@ -2860,14 +2863,17 @@
                 mPm.notifyPackageChanged(packageName, request.getAppId());
             }
 
-            for (int userId : firstUserIds) {
-                // Apply restricted settings on potentially dangerous packages. Needs to happen
-                // after appOpsManager is notified of the new package
-                if (request.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
-                        || request.getPackageSource()
-                        == PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE) {
-                    enableRestrictedSettings(packageName, request.getAppId(), userId);
-                }
+            // Apply restricted settings on potentially dangerous packages. Needs to happen
+            // after appOpsManager is notified of the new package
+            if (request.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
+                    || request.getPackageSource()
+                    == PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE) {
+                final int appId = request.getAppId();
+                mPm.mHandler.post(() -> {
+                    for (int userId : firstUserIds) {
+                        enableRestrictedSettings(packageName, appId, userId);
+                    }
+                });
             }
 
             // Log current value of "unknown sources" setting
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 243fb16..5724ee0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1085,8 +1085,14 @@
         // the sdk or package name along with optional additional information based on opt.
         final Map<String, List<String>> out = new HashMap<>();
         for (int userId : userIds) {
-            final int translatedUserId =
+            final int translatedUserId;
+            try {
+                translatedUserId =
                     translateUserId(userId, UserHandle.USER_SYSTEM, "runListPackages");
+            } catch (RuntimeException ex) {
+                getErrPrintWriter().println("Error: " + ex.toString());
+                continue;
+            }
             @SuppressWarnings("unchecked") final ParceledListSlice<PackageInfo> slice =
                     mInterface.getInstalledPackages(getFlags, translatedUserId);
             final List<PackageInfo> packages = slice.getList();
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 7c833cb..6f75439 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -481,6 +481,11 @@
         protected long mTargetDurationNanos;
         protected boolean mUpdateAllowed;
         protected int[] mNewThreadIds;
+        protected boolean mPowerEfficient;
+
+        private enum SessionModes {
+            POWER_EFFICIENCY,
+        };
 
         protected AppHintSession(
                 int uid, int pid, int[] threadIds, IBinder token,
@@ -492,6 +497,7 @@
             mHalSessionPtr = halSessionPtr;
             mTargetDurationNanos = durationNanos;
             mUpdateAllowed = true;
+            mPowerEfficient = false;
             final boolean allowed = mUidObserver.isUidForeground(mUid);
             updateHintAllowed(allowed);
             try {
@@ -634,6 +640,9 @@
                 }
                 Preconditions.checkArgument(mode >= 0, "the mode Id value should be"
                         + " greater than zero.");
+                if (mode == SessionModes.POWER_EFFICIENCY.ordinal()) {
+                    mPowerEfficient = enabled;
+                }
                 mNativeWrapper.halSetMode(mHalSessionPtr, mode, enabled);
             }
         }
@@ -653,6 +662,12 @@
             }
         }
 
+        public boolean isPowerEfficient() {
+            synchronized (this) {
+                return mPowerEfficient;
+            }
+        }
+
         void validateWorkDuration(WorkDuration workDuration) {
             if (DEBUG) {
                 Slogf.d(TAG, "WorkDuration(" + workDuration.getTimestampNanos() + ", "
@@ -718,6 +733,7 @@
                 pw.println(prefix + "SessionTIDs: " + Arrays.toString(mThreadIds));
                 pw.println(prefix + "SessionTargetDurationNanos: " + mTargetDurationNanos);
                 pw.println(prefix + "SessionAllowed: " + mUpdateAllowed);
+                pw.println(prefix + "PowerEfficient: " + (mPowerEfficient ? "true" : "false"));
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 4929df80..eed46fe 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -36,6 +36,7 @@
 import static com.android.server.wm.PendingRemoteAnimationRegistry.TIMEOUT_MS;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static java.util.Objects.requireNonNull;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -152,36 +153,25 @@
     static final int BAL_ALLOW_SDK_SANDBOX = 10;
 
     static String balCodeToString(@BalCode int balCode) {
-        switch (balCode) {
-            case BAL_ALLOW_ALLOWLISTED_COMPONENT:
-                return "BAL_ALLOW_ALLOWLISTED_COMPONENT";
-            case BAL_ALLOW_ALLOWLISTED_UID:
-                return "BAL_ALLOW_ALLOWLISTED_UID";
-            case BAL_ALLOW_DEFAULT:
-                return "BAL_ALLOW_DEFAULT";
-            case BAL_ALLOW_FOREGROUND:
-                return "BAL_ALLOW_FOREGROUND";
-            case BAL_ALLOW_GRACE_PERIOD:
-                return "BAL_ALLOW_GRACE_PERIOD";
-            case BAL_ALLOW_PENDING_INTENT:
-                return "BAL_ALLOW_PENDING_INTENT";
-            case BAL_ALLOW_PERMISSION:
-                return "BAL_ALLOW_PERMISSION";
-            case BAL_ALLOW_SAW_PERMISSION:
-                return "BAL_ALLOW_SAW_PERMISSION";
-            case BAL_ALLOW_SDK_SANDBOX:
-                return "BAL_ALLOW_SDK_SANDBOX";
-            case BAL_ALLOW_VISIBLE_WINDOW:
-                return "BAL_ALLOW_VISIBLE_WINDOW";
-            case BAL_BLOCK:
-                return "BAL_BLOCK";
-            default:
-                throw new IllegalArgumentException("Unexpected value: " + balCode);
-        }
+        return switch (balCode) {
+            case BAL_ALLOW_ALLOWLISTED_COMPONENT -> "BAL_ALLOW_ALLOWLISTED_COMPONENT";
+            case BAL_ALLOW_ALLOWLISTED_UID -> "BAL_ALLOW_ALLOWLISTED_UID";
+            case BAL_ALLOW_DEFAULT -> "BAL_ALLOW_DEFAULT";
+            case BAL_ALLOW_FOREGROUND -> "BAL_ALLOW_FOREGROUND";
+            case BAL_ALLOW_GRACE_PERIOD -> "BAL_ALLOW_GRACE_PERIOD";
+            case BAL_ALLOW_PENDING_INTENT -> "BAL_ALLOW_PENDING_INTENT";
+            case BAL_ALLOW_PERMISSION -> "BAL_ALLOW_PERMISSION";
+            case BAL_ALLOW_SAW_PERMISSION -> "BAL_ALLOW_SAW_PERMISSION";
+            case BAL_ALLOW_SDK_SANDBOX -> "BAL_ALLOW_SDK_SANDBOX";
+            case BAL_ALLOW_VISIBLE_WINDOW -> "BAL_ALLOW_VISIBLE_WINDOW";
+            case BAL_BLOCK -> "BAL_BLOCK";
+            default -> throw new IllegalArgumentException("Unexpected value: " + balCode);
+        };
     }
 
     @GuardedBy("mService.mGlobalLock")
-    private HashMap<Integer, FinishedActivityEntry> mTaskIdToFinishedActivity = new HashMap<>();
+    private final HashMap<Integer, FinishedActivityEntry> mTaskIdToFinishedActivity =
+            new HashMap<>();
     @GuardedBy("mService.mGlobalLock")
     private FinishedActivityEntry mTopFinishedActivity = null;
 
@@ -467,9 +457,8 @@
             return !blocks();
         }
 
-        BalVerdict setOnlyCreatorAllows(boolean onlyCreatorAllows) {
+        void setOnlyCreatorAllows(boolean onlyCreatorAllows) {
             mOnlyCreatorAllows = onlyCreatorAllows;
-            return this;
         }
 
         boolean onlyCreatorAllows() {
@@ -481,10 +470,6 @@
             return this;
         }
 
-        private boolean isBasedOnRealCaller() {
-            return mBasedOnRealCaller;
-        }
-
         public String toString() {
             StringBuilder builder = new StringBuilder();
             builder.append(balCodeToString(mCode));
@@ -583,15 +568,14 @@
         BalVerdict resultForCaller = checkBackgroundActivityStartAllowedByCaller(state);
 
         if (!state.hasRealCaller()) {
-            BalVerdict resultForRealCaller = null; // nothing to compute
             if (resultForCaller.allows()) {
                 if (DEBUG_ACTIVITY_STARTS) {
                     Slog.d(TAG, "Background activity start allowed. "
-                            + state.dump(resultForCaller, resultForRealCaller));
+                            + state.dump(resultForCaller, resultForCaller));
                 }
                 return statsLog(resultForCaller, state);
             }
-            return abortLaunch(state, resultForCaller, resultForRealCaller);
+            return abortLaunch(state, resultForCaller, resultForCaller);
         }
 
         // The realCaller result is only calculated for PendingIntents (indicated by a valid
@@ -653,7 +637,7 @@
                                 + " if the PI creator upgrades target_sdk to 35+"
                                 + " AND the PI sender upgrades target_sdk to 34+! "
                                 + state.dump(resultForCaller, resultForRealCaller));
-                showBalRiskToast("BAL would be blocked", state);
+                showBalRiskToast();
                 // return the realCaller result for backwards compatibility
                 return statsLog(resultForRealCaller, state);
             }
@@ -679,7 +663,7 @@
                                 + " if the PI creator upgrades target_sdk to 35+! "
                                 + " (missing opt in by PI creator)! "
                                 + state.dump(resultForCaller, resultForRealCaller));
-                showBalRiskToast("BAL would be blocked", state);
+                showBalRiskToast();
                 return statsLog(resultForCaller, state);
             }
             Slog.wtf(TAG,
@@ -696,7 +680,7 @@
                                 + " if the PI sender upgrades target_sdk to 34+! "
                                 + " (missing opt in by PI sender)! "
                                 + state.dump(resultForCaller, resultForRealCaller));
-                showBalRiskToast("BAL would be blocked", state);
+                showBalRiskToast();
                 return statsLog(resultForRealCaller, state);
             }
             Slog.wtf(TAG, "Without Android 14 BAL hardening this activity start would be allowed"
@@ -712,7 +696,7 @@
             BalVerdict resultForRealCaller) {
         Slog.w(TAG, "Background activity launch blocked! "
                 + state.dump(resultForCaller, resultForRealCaller));
-        showBalBlockedToast("BAL blocked", state);
+        showBalBlockedToast();
         return statsLog(BalVerdict.BLOCK, state);
     }
 
@@ -910,7 +894,7 @@
 
     /**
      * Check if the app allows BAL.
-     *
+     * <p>
      * See {@link BackgroundLaunchProcessController#areBackgroundActivityStartsAllowed(int, int,
      * String, int, boolean, boolean, boolean, long, long, long)} for details on the
      * exceptions.
@@ -1104,19 +1088,15 @@
         return true;
     }
 
-    private void showBalBlockedToast(String toastText, BalState state) {
+    private void showBalBlockedToast() {
         if (balShowToastsBlocked()) {
-            showToast(toastText
-                    + " caller:" + state.mCallingPackage
-                    + " realCaller:" + state.mRealCallingPackage);
+            showToast("BAL blocked. go/debug-bal");
         }
     }
 
-    private void showBalRiskToast(String toastText, BalState state) {
+    private void showBalRiskToast() {
         if (balShowToasts()) {
-            showToast(toastText
-                    + " caller:" + state.mCallingPackage
-                    + " realCaller:" + state.mRealCallingPackage);
+            showToast("BAL allowed in compat mode. go/debug-bal");
         }
     }
 
@@ -1281,7 +1261,7 @@
      * 2. Or top of an adjacent task fragment to (1)
      * <p>
      * The 'sourceRecord' can be considered top even if it is 'finishing'
-     *
+     * <p>
      * Returns a class where the elements are:
      * <pre>
      * shouldBlockActivityStart: {@code true} if we should actually block the transition (takes into
@@ -1344,7 +1324,7 @@
     /**
      * Determines if a source is allowed to add or remove activities from the task,
      * if the current ActivityRecord is above it in the stack
-     *
+     * <p>
      * A transition is blocked ({@code false} returned) if all of the following are met:
      * <pre>
      * 1. The source activity and the current activity record belong to different apps
@@ -1489,8 +1469,8 @@
 
         if (code == BAL_ALLOW_PENDING_INTENT
                 && (callingUid == Process.SYSTEM_UID || realCallingUid == Process.SYSTEM_UID)) {
-            String activityName =
-                    intent != null ? intent.getComponent().flattenToShortString() : "";
+            String activityName = intent != null
+                    ? requireNonNull(intent.getComponent()).flattenToShortString() : "";
             FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
                     activityName,
                     BAL_ALLOW_PENDING_INTENT,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ec4bdf9..32638e0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5637,6 +5637,12 @@
             // Skip sync for invisible app windows which are not managed by activity lifecycle.
             return false;
         }
+        if (mActivityRecord != null && mViewVisibility != View.VISIBLE
+                && mWinAnimator.mAttrType != TYPE_BASE_APPLICATION
+                && mWinAnimator.mAttrType != TYPE_APPLICATION_STARTING) {
+            // Skip sync for invisible app windows which are not managed by activity lifecycle.
+            return false;
+        }
         // In the WindowContainer implementation we immediately mark ready
         // since a generic WindowContainer only needs to wait for its
         // children to finish and is immediately ready from its own
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
index 49fa254..dafbbb3 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
@@ -23,8 +23,10 @@
 import org.junit.Test
 import org.mockito.junit.MockitoJUnit
 
+import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.verify
+import java.util.concurrent.Executor
 
 @SmallTest
 class DisplayPowerStateTest {
@@ -36,15 +38,21 @@
 
     private val mockBlanker = mock<DisplayBlanker>()
     private val mockColorFade = mock<ColorFade>()
+    private val mockExecutor = mock<Executor>()
 
     @Before
     fun setUp() {
-        displayPowerState = DisplayPowerState(mockBlanker, mockColorFade, 123, Display.STATE_ON)
+        displayPowerState = DisplayPowerState(mockBlanker, mockColorFade, 123, Display.STATE_ON,
+                mockExecutor)
     }
 
     @Test
     fun `destroys ColorFade on stop`() {
         displayPowerState.stop()
+        val runnableCaptor = argumentCaptor<Runnable>()
+
+        verify(mockExecutor).execute(runnableCaptor.capture())
+        runnableCaptor.firstValue.run()
 
         verify(mockColorFade).destroy()
     }
diff --git a/services/tests/servicestests/src/com/android/server/OWNERS b/services/tests/servicestests/src/com/android/server/OWNERS
index 2d36ff3..d49bc43 100644
--- a/services/tests/servicestests/src/com/android/server/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/OWNERS
@@ -7,3 +7,4 @@
 per-file BatteryServiceTest.java = file:platform/hardware/interfaces:/health/OWNERS
 per-file GestureLauncherServiceTest.java = file:platform/packages/apps/EmergencyInfo:/OWNERS
 per-file PinnerServiceTest.java = file:/apct-tests/perftests/OWNERS
+per-file SecurityStateTest.java = file:/SECURITY_STATE_OWNERS
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index 2868b7e..ae36839 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -68,10 +68,7 @@
 import android.os.Parcel;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.INotificationListener;
 import android.service.notification.NotificationListenerFilter;
 import android.service.notification.NotificationListenerService;
@@ -111,7 +108,7 @@
 public class NotificationListenersTest extends UiServiceTestCase {
 
     @Rule
-    public CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+    public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     @Mock
     private PackageManager mPm;
@@ -696,8 +693,8 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
     public void testListenerTrusted_withPermission() throws RemoteException {
+        mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
         when(mNm.mPackageManager.checkUidPermission(RECEIVE_SENSITIVE_NOTIFICATIONS, mUid1))
                 .thenReturn(PERMISSION_GRANTED);
         ManagedServices.ManagedServiceInfo info = getMockServiceInfo();
@@ -706,8 +703,8 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
     public void testListenerTrusted_withSystemSignature() {
+        mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
         when(mNm.mPackageManagerInternal.isPlatformSigned(mCn1.getPackageName())).thenReturn(true);
         ManagedServices.ManagedServiceInfo info = getMockServiceInfo();
         mListeners.onServiceAdded(info);
@@ -715,8 +712,8 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
     public void testListenerTrusted_withCdmAssociation() throws Exception {
+        mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
         mNm.mCompanionManager = mock(ICompanionDeviceManager.class);
         AssociationInfo assocInfo = mock(AssociationInfo.class);
         when(assocInfo.isRevoked()).thenReturn(false);
@@ -731,16 +728,16 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
     public void testListenerTrusted_ifFlagDisabled() {
+        mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
         ManagedServices.ManagedServiceInfo info = getMockServiceInfo();
         mListeners.onServiceAdded(info);
         assertTrue(mListeners.isUidTrusted(mUid1));
     }
 
     @Test
-    @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
     public void testRedaction_whenPosted() {
+        mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
         ArrayList<ManagedServices.ManagedServiceInfo> infos = new ArrayList<>();
         infos.add(getMockServiceInfo());
         doReturn(infos).when(mListeners).getServices();
@@ -762,13 +759,11 @@
         mListeners.notifyPostedLocked(r, old);
         verify(mListeners, atLeast(1)).redactStatusBarNotification(eq(sbn));
         verify(mListeners, never()).redactStatusBarNotification(eq(oldSbn));
-
-
     }
 
     @Test
-    @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
     public void testRedaction_whenPosted_oldRemoved() {
+        mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
         ArrayList<ManagedServices.ManagedServiceInfo> infos = new ArrayList<>();
         infos.add(getMockServiceInfo());
         doReturn(infos).when(mListeners).getServices();
@@ -795,8 +790,8 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
     public void testRedaction_whenRemoved() {
+        mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
         doReturn(mock(StatusBarNotification.class))
                 .when(mListeners).redactStatusBarNotification(any());
         ArrayList<ManagedServices.ManagedServiceInfo> infos = new ArrayList<>();
@@ -816,8 +811,8 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
     public void testRedaction_noneIfFlagDisabled() {
+        mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
         ArrayList<ManagedServices.ManagedServiceInfo> infos = new ArrayList<>();
         infos.add(getMockServiceInfo());
         doReturn(infos).when(mListeners).getServices();
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 a0e49a6..9408a8b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -77,7 +77,9 @@
 import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
 import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.service.notification.Adjustment.KEY_CONTEXTUAL_ACTIONS;
 import static android.service.notification.Adjustment.KEY_IMPORTANCE;
+import static android.service.notification.Adjustment.KEY_TEXT_REPLIES;
 import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
 import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
 import static android.service.notification.Condition.SOURCE_CONTEXT;
@@ -216,9 +218,6 @@
 import android.os.WorkSource;
 import android.permission.PermissionManager;
 import android.platform.test.annotations.EnableFlags;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.platform.test.rule.DeniedDevices;
 import android.platform.test.rule.DeviceProduct;
@@ -361,9 +360,6 @@
     @Rule
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
 
-    @Rule
-    public CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
     private TestableNotificationManagerService mService;
     private INotificationManager mBinderService;
     private NotificationManagerInternal mInternalService;
@@ -11778,8 +11774,8 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
     public void testGetActiveNotificationsFromListener_redactNotification() throws Exception {
+        mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
         NotificationRecord r =
                 generateNotificationRecord(mTestNotificationChannel, 0, 0);
         mService.addNotification(r);
@@ -11808,12 +11804,11 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
     public void testGetSnoozedNotificationsFromListener_redactNotification() throws Exception {
+        mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
         NotificationRecord r =
                 generateNotificationRecord(mTestNotificationChannel, 0, 0);
-        mService.addNotification(r);
-        mService.snoozeNotificationInt(r.getKey(), 1000, null, mListener);
+        when(mSnoozeHelper.getSnoozed()).thenReturn(List.of(r));
         when(mListeners.isUidTrusted(anyInt())).thenReturn(false);
         when(mListeners.hasSensitiveContent(any())).thenReturn(true);
         StatusBarNotification redacted = generateRedactedSbn(mTestNotificationChannel, 1, 1);
@@ -11993,6 +11988,97 @@
     }
 
     @Test
+    public void testMakeRankingUpdate_redactsIfRecordSensitiveAndServiceUntrusted() {
+        mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
+        when(mListeners.isUidTrusted(anyInt())).thenReturn(false);
+        when(mListeners.hasSensitiveContent(any())).thenReturn(true);
+        NotificationRecord pkgA = new NotificationRecord(mContext,
+                generateSbn("a", 1000, 9, 0), mTestNotificationChannel);
+        addSmartActionsAndReplies(pkgA);
+        mService.addNotification(pkgA);
+        NotificationRecord pkgB = new NotificationRecord(mContext,
+                generateSbn("b", 1001, 9, 0), mTestNotificationChannel);
+        addSmartActionsAndReplies(pkgB);
+        mService.addNotification(pkgB);
+
+        ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
+        when(info.enabledAndUserMatches(anyInt())).thenReturn(true);
+        when(info.isSameUser(anyInt())).thenReturn(true);
+        NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(info);
+        NotificationListenerService.Ranking ranking =
+                nru.getRankingMap().getRawRankingObject(pkgA.getSbn().getKey());
+        assertEquals(0, ranking.getSmartActions().size());
+        assertEquals(0, ranking.getSmartReplies().size());
+        NotificationListenerService.Ranking ranking2 =
+                nru.getRankingMap().getRawRankingObject(pkgB.getSbn().getKey());
+        assertEquals(0, ranking2.getSmartActions().size());
+        assertEquals(0, ranking2.getSmartReplies().size());
+    }
+
+    @Test
+    public void testMakeRankingUpdate_doestntRedactIfFlagDisabled() {
+        mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
+        when(mListeners.isUidTrusted(anyInt())).thenReturn(false);
+        when(mListeners.hasSensitiveContent(any())).thenReturn(true);
+        NotificationRecord pkgA = new NotificationRecord(mContext,
+                generateSbn("a", 1000, 9, 0), mTestNotificationChannel);
+        addSmartActionsAndReplies(pkgA);
+
+        mService.addNotification(pkgA);
+        ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
+        when(info.enabledAndUserMatches(anyInt())).thenReturn(true);
+        when(info.isSameUser(anyInt())).thenReturn(true);
+        NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(info);
+        NotificationListenerService.Ranking ranking =
+                nru.getRankingMap().getRawRankingObject(pkgA.getSbn().getKey());
+        assertEquals(1, ranking.getSmartActions().size());
+        assertEquals(1, ranking.getSmartReplies().size());
+    }
+
+    @Test
+    public void testMakeRankingUpdate_doesntRedactIfNotSensitiveOrServiceTrusted() {
+        mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
+        NotificationRecord pkgA = new NotificationRecord(mContext,
+                generateSbn("a", 1000, 9, 0), mTestNotificationChannel);
+        addSmartActionsAndReplies(pkgA);
+
+        mService.addNotification(pkgA);
+        ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
+        when(info.enabledAndUserMatches(anyInt())).thenReturn(true);
+        when(info.isSameUser(anyInt())).thenReturn(true);
+
+        // No sensitive content, no redaction
+        when(mListeners.isUidTrusted(eq(1000))).thenReturn(false);
+        when(mListeners.hasSensitiveContent(any())).thenReturn(false);
+        NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(info);
+        NotificationListenerService.Ranking ranking =
+                nru.getRankingMap().getRawRankingObject(pkgA.getSbn().getKey());
+        assertEquals(1, ranking.getSmartActions().size());
+        assertEquals(1, ranking.getSmartReplies().size());
+
+        // trusted listener, no redaction
+        when(mListeners.isUidTrusted(eq(1000))).thenReturn(true);
+        when(mListeners.hasSensitiveContent(any())).thenReturn(true);
+        nru = mService.makeRankingUpdateLocked(info);
+        ranking = nru.getRankingMap().getRawRankingObject(pkgA.getSbn().getKey());
+        assertEquals(1, ranking.getSmartActions().size());
+        assertEquals(1, ranking.getSmartReplies().size());
+    }
+
+    private void addSmartActionsAndReplies(NotificationRecord record) {
+        Bundle b = new Bundle();
+        ArrayList<Notification.Action> actions = new ArrayList<>();
+        actions.add(new Notification.Action(0, "", null));
+        b.putParcelableArrayList(KEY_CONTEXTUAL_ACTIONS, actions);
+        ArrayList<CharSequence> replies = new ArrayList<>(List.of("test"));
+        b.putCharSequenceArrayList(KEY_TEXT_REPLIES, replies);
+        Adjustment a = new Adjustment(record.getSbn().getPackageName(), record.getSbn().getKey(),
+                b, "", record.getUserId());
+        record.addAdjustment(a);
+        record.applyAdjustments();
+    }
+
+    @Test
     public void testMaybeShowReviewPermissionsNotification_flagOff() {
         mService.setShowReviewPermissionsNotification(false);
         reset(mMockNm);
@@ -13616,7 +13702,31 @@
 
     @Test
     @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
-    public void setNotificationPolicy_watchCompanionApp_setsGlobalPolicy() throws RemoteException {
+    public void setNotificationPolicy_watchCompanionApp_setsGlobalPolicy()
+            throws RemoteException {
+        setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy(
+                AssociationRequest.DEVICE_PROFILE_WATCH, true);
+    }
+
+    @Test
+    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+    public void setNotificationPolicy_autoCompanionApp_setsGlobalPolicy()
+            throws RemoteException {
+        setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy(
+                AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, true);
+    }
+
+    @Test
+    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+    public void setNotificationPolicy_otherCompanionApp_doesNotSetGlobalPolicy()
+            throws RemoteException {
+        setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy(
+                AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, false);
+    }
+
+    private void setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy(
+            @AssociationRequest.DeviceProfile String deviceProfile, boolean canSetGlobalPolicy)
+            throws RemoteException {
         mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mService.setCallerIsNormalPackage();
         ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
@@ -13626,14 +13736,19 @@
         when(mCompanionMgr.getAssociations(anyString(), anyInt()))
                 .thenReturn(ImmutableList.of(
                         new AssociationInfo.Builder(1, mUserId, "package")
-                                .setDisplayName("My watch")
-                                .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH)
+                                .setDisplayName("My connected device")
+                                .setDeviceProfile(deviceProfile)
                                 .build()));
 
         NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
         mBinderService.setNotificationPolicy("package", policy, false);
 
-        verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
+        if (canSetGlobalPolicy) {
+            verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
+        } else {
+            verify(zenModeHelper).applyGlobalPolicyAsImplicitZenRule(anyString(), anyInt(),
+                    eq(policy), anyInt());
+        }
     }
 
     @Test
@@ -13703,7 +13818,29 @@
 
     @Test
     @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
-    public void setInterruptionFilter_watchCompanionApp_setsGlobalPolicy() throws RemoteException {
+    public void setInterruptionFilter_watchCompanionApp_setsGlobalZen() throws RemoteException {
+        setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen(
+                AssociationRequest.DEVICE_PROFILE_WATCH, true);
+    }
+
+    @Test
+    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+    public void setInterruptionFilter_autoCompanionApp_setsGlobalZen() throws RemoteException {
+        setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen(
+                AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, true);
+    }
+
+    @Test
+    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+    public void setInterruptionFilter_otherCompanionApp_doesNotSetGlobalZen()
+            throws RemoteException {
+        setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen(
+                AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, false);
+    }
+
+    private void setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen(
+            @AssociationRequest.DeviceProfile String deviceProfile, boolean canSetGlobalPolicy)
+            throws RemoteException {
         mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
         mService.mZenModeHelper = zenModeHelper;
@@ -13713,14 +13850,19 @@
         when(mCompanionMgr.getAssociations(anyString(), anyInt()))
                 .thenReturn(ImmutableList.of(
                         new AssociationInfo.Builder(1, mUserId, "package")
-                                .setDisplayName("My watch")
-                                .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH)
+                                .setDisplayName("My connected device")
+                                .setDeviceProfile(deviceProfile)
                                 .build()));
 
         mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY, false);
 
-        verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
-                eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyString(), eq("package"), anyInt());
+        if (canSetGlobalPolicy) {
+            verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
+                    eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyString(), eq("package"), anyInt());
+        } else {
+            verify(zenModeHelper).applyGlobalZenModeAsImplicitZenRule(anyString(), anyInt(),
+                    eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS));
+        }
     }
 
     @Test
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 4c978ad..2445f51 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -28,6 +28,7 @@
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
 import android.app.usage.ExternalStorageStats;
+import android.app.usage.Flags;
 import android.app.usage.IStorageStatsManager;
 import android.app.usage.StorageStats;
 import android.app.usage.UsageStatsManagerInternal;
@@ -434,6 +435,7 @@
         final long[] ceDataInodes = new long[packageNames.length];
         String[] codePaths = new String[0];
 
+        final PackageStats stats = new PackageStats(TAG);
         for (int i = 0; i < packageNames.length; i++) {
             try {
                 final ApplicationInfo appInfo = mPackage.getApplicationInfoAsUser(packageNames[i],
@@ -443,7 +445,11 @@
                 } else {
                     if (appInfo.getCodePath() != null) {
                         codePaths = ArrayUtils.appendElement(String.class, codePaths,
-                                appInfo.getCodePath());
+                            appInfo.getCodePath());
+                    }
+                    if (Flags.getAppBytesByDataTypeApi()) {
+                        computeAppStatsByDataTypes(
+                            stats, appInfo.sourceDir);
                     }
                 }
             } catch (NameNotFoundException e) {
@@ -451,7 +457,6 @@
             }
         }
 
-        final PackageStats stats = new PackageStats(TAG);
         try {
             mInstaller.getAppSize(volumeUuid, packageNames, userId, getDefaultFlags(),
                     appId, ceDataInodes, codePaths, stats);
@@ -587,6 +592,9 @@
         res.codeBytes = stats.codeSize + stats.externalCodeSize;
         res.dataBytes = stats.dataSize + stats.externalDataSize;
         res.cacheBytes = stats.cacheSize + stats.externalCacheSize;
+        res.apkBytes = stats.apkSize;
+        res.libBytes = stats.libSize;
+        res.dmBytes = stats.dmSize;
         res.externalCacheBytes = stats.externalCacheSize;
         return res;
     }
@@ -894,4 +902,61 @@
             mStorageStatsAugmenters.add(Pair.create(tag, storageStatsAugmenter));
         }
     }
+
+    private long getDirBytes(File dir) {
+        if (!dir.isDirectory()) {
+            return 0;
+        }
+
+        long size = 0;
+        try {
+            for (File file : dir.listFiles()) {
+                if (file.isFile()) {
+                    size += file.length();
+                    continue;
+                }
+                if (file.isDirectory()) {
+                    size += getDirBytes(file);
+                }
+            }
+        } catch (NullPointerException e) {
+            Slog.w(TAG, "Failed to list directory " + dir.getName());
+        }
+
+        return size;
+    }
+
+    private long getFileBytesInDir(File dir, String suffix) {
+        if (!dir.isDirectory()) {
+            return 0;
+        }
+
+        long size = 0;
+        try {
+            for (File file : dir.listFiles()) {
+                if (file.isFile() && file.getName().endsWith(suffix)) {
+                    size += file.length();
+                }
+            }
+        } catch (NullPointerException e) {
+             Slog.w(TAG, "Failed to list directory " + dir.getName());
+        }
+
+        return size;
+    }
+
+    private void computeAppStatsByDataTypes(
+        PackageStats stats, String sourceDirName) {
+
+        // Get apk, lib, dm file sizes.
+        File srcDir = new File(sourceDirName);
+        if (srcDir.isFile()) {
+            sourceDirName = srcDir.getParent();
+            srcDir = new File(sourceDirName);
+        }
+
+        stats.apkSize += getFileBytesInDir(srcDir, ".apk");
+        stats.dmSize += getFileBytesInDir(srcDir, ".dm");
+        stats.libSize += getDirBytes(new File(sourceDirName + "/lib/"));
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index ccd4ce0..08f719e 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1121,13 +1121,8 @@
 
             switch (event.mEventType) {
                 case Event.ACTIVITY_RESUMED:
-                    FrameworkStatsLog.write(
-                            FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED,
-                            uid,
-                            event.mPackage,
-                            "",
-                            FrameworkStatsLog
-                                    .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_FOREGROUND);
+                    logAppUsageEventReportedAtomLocked(Event.ACTIVITY_RESUMED, uid, event.mPackage);
+
                     // check if this activity has already been resumed
                     if (mVisibleActivities.get(event.mInstanceId) != null) break;
                     final String usageSourcePackage = getUsageSourcePackage(event);
@@ -1172,13 +1167,8 @@
                                 usageSourcePackage2);
                         mVisibleActivities.put(event.mInstanceId, pausedData);
                     } else {
-                        FrameworkStatsLog.write(
-                                FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED,
-                                uid,
-                                event.mPackage,
-                                "",
-                                FrameworkStatsLog
-                                        .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_BACKGROUND);
+                        logAppUsageEventReportedAtomLocked(Event.ACTIVITY_PAUSED, uid,
+                                event.mPackage);
                     }
 
                     pausedData.lastEvent = Event.ACTIVITY_PAUSED;
@@ -1203,13 +1193,8 @@
                     }
 
                     if (prevData.lastEvent != Event.ACTIVITY_PAUSED) {
-                        FrameworkStatsLog.write(
-                                FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED,
-                                uid,
-                                event.mPackage,
-                                "",
-                                FrameworkStatsLog
-                                        .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_BACKGROUND);
+                        logAppUsageEventReportedAtomLocked(Event.ACTIVITY_PAUSED, uid,
+                                event.mPackage);
                     }
 
                     ArraySet<String> tokens;
@@ -1244,11 +1229,19 @@
                     }
                     break;
                 case Event.USER_INTERACTION:
-                    // Fall through
+                    logAppUsageEventReportedAtomLocked(Event.USER_INTERACTION, uid, event.mPackage);
+                    // Fall through.
                 case Event.APP_COMPONENT_USED:
                     convertToSystemTimeLocked(event);
                     mLastTimeComponentUsedGlobal.put(event.mPackage, event.mTimeStamp);
                     break;
+                case Event.SHORTCUT_INVOCATION:
+                case Event.CHOOSER_ACTION:
+                case Event.STANDBY_BUCKET_CHANGED:
+                case Event.FOREGROUND_SERVICE_START:
+                case Event.FOREGROUND_SERVICE_STOP:
+                    logAppUsageEventReportedAtomLocked(event.mEventType, uid, event.mPackage);
+                    break;
             }
 
             final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
@@ -1261,6 +1254,45 @@
         mIoHandler.obtainMessage(MSG_NOTIFY_USAGE_EVENT_LISTENER, userId, 0, event).sendToTarget();
     }
 
+    @GuardedBy("mLock")
+    private void logAppUsageEventReportedAtomLocked(int eventType, int uid, String packageName) {
+        FrameworkStatsLog.write(FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED, uid, packageName,
+                "", getAppUsageEventOccurredAtomEventType(eventType));
+    }
+
+    /** Make sure align with the EventType defined in the AppUsageEventOccurred atom. */
+    private int getAppUsageEventOccurredAtomEventType(int eventType) {
+        switch (eventType) {
+            case Event.ACTIVITY_RESUMED:
+                return FrameworkStatsLog
+                        .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_FOREGROUND;
+            case Event.ACTIVITY_PAUSED:
+                return FrameworkStatsLog
+                        .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_BACKGROUND;
+            case Event.USER_INTERACTION:
+                return FrameworkStatsLog
+                        .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__USER_INTERACTION;
+            case Event.SHORTCUT_INVOCATION:
+                return FrameworkStatsLog
+                        .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__SHORTCUT_INVOCATION;
+            case Event.CHOOSER_ACTION:
+                return FrameworkStatsLog
+                        .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__CHOOSER_ACTION;
+            case Event.STANDBY_BUCKET_CHANGED:
+                return FrameworkStatsLog
+                        .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__STANDBY_BUCKET_CHANGED;
+            case Event.FOREGROUND_SERVICE_START:
+                return FrameworkStatsLog
+                        .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__FOREGROUND_SERVICE_START;
+            case Event.FOREGROUND_SERVICE_STOP:
+                return FrameworkStatsLog
+                        .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__FOREGROUND_SERVICE_STOP;
+            default:
+                Slog.w(TAG, "Unsupported usage event logging: " + eventType);
+                return -1;
+        }
+    }
+
     private String getUsageSourcePackage(Event event) {
         switch(mUsageSource) {
             case USAGE_SOURCE_CURRENT_ACTIVITY:
diff --git a/telecomm/java/android/telecom/AuthenticatorService.java b/telecomm/java/android/telecom/AuthenticatorService.java
deleted file mode 100644
index 1e43c71..0000000
--- a/telecomm/java/android/telecom/AuthenticatorService.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2015 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.telecom;
-import android.accounts.AbstractAccountAuthenticator;
-import android.accounts.Account;
-import android.accounts.AccountAuthenticatorResponse;
-import android.accounts.NetworkErrorException;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-
-/**
- * A generic stub account authenticator service often used for sync adapters that do not directly
- * involve accounts.
- *
- * @hide
- */
-public class AuthenticatorService extends Service {
-    private static Authenticator mAuthenticator;
-
-    @Override
-    public void onCreate() {
-        mAuthenticator = new Authenticator(this);
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mAuthenticator.getIBinder();
-    }
-
-    /**
-     * Stub account authenticator. All methods either return null or throw an exception.
-     */
-    public class Authenticator extends AbstractAccountAuthenticator {
-        public Authenticator(Context context) {
-            super(context);
-        }
-
-        @Override
-        public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse,
-                                     String s) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse,
-                                 String s, String s2, String[] strings, Bundle bundle)
-                throws NetworkErrorException {
-            return null;
-        }
-
-        @Override
-        public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
-                                         Account account, Bundle bundle)
-                throws NetworkErrorException {
-            return null;
-        }
-
-        @Override
-        public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse,
-                                   Account account, String s, Bundle bundle)
-                throws NetworkErrorException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public String getAuthTokenLabel(String s) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
-                                        Account account, String s, Bundle bundle)
-                throws NetworkErrorException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse,
-                                  Account account, String[] strings)
-                throws NetworkErrorException {
-            throw new UnsupportedOperationException();
-        }
-    }
-}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index ee9bf898..a2105b0 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -3340,15 +3340,6 @@
     public void onDisconnect() {}
 
     /**
-     * Notifies this Connection of a request to disconnect a participant of the conference managed
-     * by the connection.
-     *
-     * @param endpoint the {@link Uri} of the participant to disconnect.
-     * @hide
-     */
-    public void onDisconnectConferenceParticipant(Uri endpoint) {}
-
-    /**
      * Notifies this Connection of a request to separate from its parent conference.
      */
     public void onSeparate() {}